mirror of
https://github.com/CloudNebulaProject/vm-manager.git
synced 2026-04-10 05:10:41 +00:00
Generate per-VM Ed25519 SSH keypairs instead of requiring user keys
libssh2 cannot handle all OpenSSH private key formats (e.g. passphrase- protected or newer ed25519 keys), causing auth failures. Instead of referencing the user's ~/.ssh keys, generate a fresh Ed25519 keypair at resolve time when the VMFile omits ssh-key and private-key. The public key is injected into cloud-init and the private PEM is persisted to the VM's work directory so that provision, reload, and ssh commands can reuse it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d9a4206447
commit
4cf35c99d0
11 changed files with 670 additions and 103 deletions
454
Cargo.lock
generated
454
Cargo.lock
generated
|
|
@ -127,12 +127,24 @@ dependencies = [
|
|||
"backtrace",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base16ct"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
|
@ -209,6 +221,16 @@ dependencies = [
|
|||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.58"
|
||||
|
|
@ -255,6 +277,12 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.10.1"
|
||||
|
|
@ -289,6 +317,18 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.7"
|
||||
|
|
@ -299,12 +339,48 @@ dependencies = [
|
|||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "4.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"curve25519-dalek-derive",
|
||||
"digest",
|
||||
"fiat-crypto",
|
||||
"rustc_version",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek-derive"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
|
|
@ -312,7 +388,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"const-oid",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -347,6 +425,60 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ecdsa"
|
||||
version = "0.16.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
|
||||
dependencies = [
|
||||
"der",
|
||||
"digest",
|
||||
"elliptic-curve",
|
||||
"rfc6979",
|
||||
"signature",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519"
|
||||
version = "2.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
|
||||
dependencies = [
|
||||
"signature",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519-dalek"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"ed25519",
|
||||
"sha2",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
version = "0.13.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"crypto-bigint",
|
||||
"digest",
|
||||
"ff",
|
||||
"generic-array",
|
||||
"group",
|
||||
"pkcs8",
|
||||
"rand_core 0.6.4",
|
||||
"sec1",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
|
|
@ -381,6 +513,22 @@ dependencies = [
|
|||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fiat-crypto"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
|
|
@ -471,6 +619,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
|||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -519,6 +668,17 @@ version = "0.32.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
|
||||
dependencies = [
|
||||
"ff",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
|
|
@ -540,6 +700,15 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.4.0"
|
||||
|
|
@ -784,6 +953,15 @@ dependencies = [
|
|||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.11.0"
|
||||
|
|
@ -867,6 +1045,9 @@ name = "lazy_static"
|
|||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
|
|
@ -880,6 +1061,12 @@ version = "0.2.182"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.12"
|
||||
|
|
@ -1047,6 +1234,22 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint-dig"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libm",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-traits",
|
||||
"rand 0.8.5",
|
||||
"smallvec",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.6"
|
||||
|
|
@ -1094,6 +1297,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1147,6 +1351,44 @@ version = "4.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52"
|
||||
|
||||
[[package]]
|
||||
name = "p256"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
|
||||
dependencies = [
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"primeorder",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "p384"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6"
|
||||
dependencies = [
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"primeorder",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "p521"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"primeorder",
|
||||
"rand_core 0.6.4",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.5"
|
||||
|
|
@ -1170,6 +1412,15 @@ dependencies = [
|
|||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem-rfc7468"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.2"
|
||||
|
|
@ -1188,6 +1439,27 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkcs1"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
|
||||
dependencies = [
|
||||
"der",
|
||||
"pkcs8",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.32"
|
||||
|
|
@ -1222,6 +1494,15 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "primeorder"
|
||||
version = "0.13.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
|
||||
dependencies = [
|
||||
"elliptic-curve",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
|
|
@ -1260,7 +1541,7 @@ dependencies = [
|
|||
"bytes",
|
||||
"getrandom 0.3.4",
|
||||
"lru-slab",
|
||||
"rand",
|
||||
"rand 0.9.2",
|
||||
"ring",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
|
|
@ -1301,14 +1582,34 @@ version = "5.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1318,7 +1619,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1420,6 +1730,16 @@ dependencies = [
|
|||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfc6979"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
|
||||
dependencies = [
|
||||
"hmac",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.14"
|
||||
|
|
@ -1434,6 +1754,27 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"digest",
|
||||
"num-bigint-dig",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"pkcs1",
|
||||
"pkcs8",
|
||||
"rand_core 0.6.4",
|
||||
"sha2",
|
||||
"signature",
|
||||
"spki",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.27"
|
||||
|
|
@ -1446,6 +1787,15 @@ version = "2.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.3"
|
||||
|
|
@ -1533,6 +1883,20 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "sec1"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"der",
|
||||
"generic-array",
|
||||
"pkcs8",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "3.6.0"
|
||||
|
|
@ -1628,6 +1992,17 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
|
|
@ -1653,6 +2028,16 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.12"
|
||||
|
|
@ -1675,6 +2060,64 @@ dependencies = [
|
|||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ssh-cipher"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
"ssh-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ssh-encoding"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"pem-rfc7468",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ssh-key"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b86f5297f0f04d08cabaa0f6bff7cb6aec4d9c3b49d87990d63da9d9156a8c3"
|
||||
dependencies = [
|
||||
"ed25519-dalek",
|
||||
"p256",
|
||||
"p384",
|
||||
"p521",
|
||||
"rand_core 0.6.4",
|
||||
"rsa",
|
||||
"sec1",
|
||||
"sha2",
|
||||
"signature",
|
||||
"ssh-cipher",
|
||||
"ssh-encoding",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ssh2"
|
||||
version = "0.9.5"
|
||||
|
|
@ -2029,7 +2472,7 @@ dependencies = [
|
|||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand",
|
||||
"rand 0.9.2",
|
||||
"sha1",
|
||||
"thiserror",
|
||||
"utf-8",
|
||||
|
|
@ -2150,6 +2593,7 @@ dependencies = [
|
|||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"ssh-key",
|
||||
"ssh2",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
|
|
|
|||
|
|
@ -36,3 +36,4 @@ futures-util = "0.3"
|
|||
zstd = "0.13"
|
||||
dirs = "6"
|
||||
kdl = "6"
|
||||
ssh-key = { version = "0.6", features = ["ed25519", "rand_core", "getrandom"] }
|
||||
|
|
|
|||
|
|
@ -6,12 +6,10 @@ vm "omnios-builder" {
|
|||
|
||||
cloud-init {
|
||||
hostname "omnios-builder"
|
||||
ssh-key "~/.ssh/id_ed25519.pub"
|
||||
}
|
||||
|
||||
ssh {
|
||||
user "smithy"
|
||||
private-key "~/.ssh/id_ed25519"
|
||||
}
|
||||
|
||||
// Stage 1: System packages and Rust toolchain
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ isobemak = { version = "0.2", optional = true }
|
|||
|
||||
# SSH
|
||||
ssh2 = "0.9"
|
||||
ssh-key.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
libc = "0.2"
|
||||
|
|
|
|||
|
|
@ -78,6 +78,13 @@ pub enum VmError {
|
|||
)]
|
||||
SshFailed { detail: String },
|
||||
|
||||
#[error("failed to generate SSH keypair: {detail}")]
|
||||
#[diagnostic(
|
||||
code(vm_manager::ssh::keygen_failed),
|
||||
help("this is an internal error in Ed25519 key generation — please report it")
|
||||
)]
|
||||
SshKeygenFailed { detail: String },
|
||||
|
||||
#[error("failed to download image from {url}: {detail}")]
|
||||
#[diagnostic(
|
||||
code(vm_manager::image::download_failed),
|
||||
|
|
|
|||
|
|
@ -66,7 +66,9 @@ pub struct CloudInitDef {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct SshDef {
|
||||
pub user: String,
|
||||
pub private_key: String,
|
||||
/// Path to an existing private key file. When `None`, a per-VM Ed25519
|
||||
/// keypair is generated at resolve time and used via in-memory PEM.
|
||||
pub private_key: Option<String>,
|
||||
}
|
||||
|
||||
/// A provisioning step.
|
||||
|
|
@ -320,7 +322,7 @@ fn parse_vm_def(name: &str, doc: &KdlDocument) -> Result<VmDef> {
|
|||
let ssh_doc = ssh_node.children().ok_or_else(|| VmError::VmFileValidation {
|
||||
vm: name.into(),
|
||||
detail: "ssh block must have a body".into(),
|
||||
hint: "add user and private-key inside: ssh { user \"vm\"; private-key \"~/.ssh/id_ed25519\" }".into(),
|
||||
hint: "add at least a user: ssh { user \"vm\" }".into(),
|
||||
})?;
|
||||
let user = ssh_doc
|
||||
.get_arg("user")
|
||||
|
|
@ -330,12 +332,7 @@ fn parse_vm_def(name: &str, doc: &KdlDocument) -> Result<VmDef> {
|
|||
let private_key = ssh_doc
|
||||
.get_arg("private-key")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or_else(|| VmError::VmFileValidation {
|
||||
vm: name.into(),
|
||||
detail: "ssh block requires private-key".into(),
|
||||
hint: "add: private-key \"~/.ssh/id_ed25519\"".into(),
|
||||
})?
|
||||
.to_string();
|
||||
.map(String::from);
|
||||
Some(SshDef { user, private_key })
|
||||
} else {
|
||||
None
|
||||
|
|
@ -465,60 +462,8 @@ pub async fn resolve(def: &VmDef, base_dir: &Path) -> Result<VmSpec> {
|
|||
NetworkDef::None => NetworkConfig::None,
|
||||
};
|
||||
|
||||
// Cloud-init
|
||||
let cloud_init = if let Some(ci) = &def.cloud_init {
|
||||
if let Some(raw_path) = &ci.user_data {
|
||||
// Raw user-data file
|
||||
let p = resolve_path(raw_path, base_dir);
|
||||
let data = tokio::fs::read(&p)
|
||||
.await
|
||||
.map_err(|e| VmError::VmFileValidation {
|
||||
vm: def.name.clone(),
|
||||
detail: format!("cannot read user-data at {}: {e}", p.display()),
|
||||
hint: "check the user-data path".into(),
|
||||
})?;
|
||||
Some(CloudInitConfig {
|
||||
user_data: data,
|
||||
instance_id: Some(def.name.clone()),
|
||||
hostname: ci.hostname.clone().or_else(|| Some(def.name.clone())),
|
||||
})
|
||||
} else if let Some(key_raw) = &ci.ssh_key {
|
||||
// Build cloud-config from SSH key
|
||||
let key_path = resolve_path(key_raw, base_dir);
|
||||
let pubkey = tokio::fs::read_to_string(&key_path).await.map_err(|e| {
|
||||
VmError::VmFileValidation {
|
||||
vm: def.name.clone(),
|
||||
detail: format!("cannot read ssh-key at {}: {e}", key_path.display()),
|
||||
hint: "check the ssh-key path".into(),
|
||||
}
|
||||
})?;
|
||||
let hostname = ci.hostname.as_deref().unwrap_or(&def.name);
|
||||
let ssh_user = def.ssh.as_ref().map(|s| s.user.as_str()).unwrap_or("vm");
|
||||
let (user_data, _meta) =
|
||||
build_cloud_config(ssh_user, pubkey.trim(), &def.name, hostname);
|
||||
Some(CloudInitConfig {
|
||||
user_data,
|
||||
instance_id: Some(def.name.clone()),
|
||||
hostname: Some(hostname.to_string()),
|
||||
})
|
||||
} else {
|
||||
// cloud-init block with only hostname, no keys or user-data
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// SSH config
|
||||
let ssh = def.ssh.as_ref().map(|s| {
|
||||
let key_path = resolve_path(&s.private_key, base_dir);
|
||||
SshConfig {
|
||||
user: s.user.clone(),
|
||||
public_key: None,
|
||||
private_key_path: Some(key_path),
|
||||
private_key_pem: None,
|
||||
}
|
||||
});
|
||||
// Cloud-init + SSH config (resolved together because key generation affects both)
|
||||
let (cloud_init, ssh) = resolve_cloud_init_and_ssh(def, base_dir).await?;
|
||||
|
||||
Ok(VmSpec {
|
||||
name: def.name.clone(),
|
||||
|
|
@ -532,6 +477,134 @@ pub async fn resolve(def: &VmDef, base_dir: &Path) -> Result<VmSpec> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Generate an Ed25519 SSH keypair and return `(public_key_openssh, private_key_pem)`.
|
||||
fn generate_ssh_keypair(vm_name: &str) -> Result<(String, String)> {
|
||||
use ssh_key::{Algorithm, LineEnding, PrivateKey, rand_core::OsRng};
|
||||
|
||||
let sk = PrivateKey::random(&mut OsRng, Algorithm::Ed25519).map_err(|e| {
|
||||
VmError::SshKeygenFailed {
|
||||
detail: format!("Ed25519 key generation for VM '{vm_name}': {e}"),
|
||||
}
|
||||
})?;
|
||||
|
||||
let pub_openssh = sk.public_key().to_openssh().map_err(|e| {
|
||||
VmError::SshKeygenFailed {
|
||||
detail: format!("serialize public key: {e}"),
|
||||
}
|
||||
})?;
|
||||
|
||||
let priv_pem = sk.to_openssh(LineEnding::LF).map_err(|e| {
|
||||
VmError::SshKeygenFailed {
|
||||
detail: format!("serialize private key: {e}"),
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok((pub_openssh, priv_pem.to_string()))
|
||||
}
|
||||
|
||||
/// Resolve cloud-init and SSH config together.
|
||||
///
|
||||
/// When the VMFile provides a `cloud-init` block but no `ssh-key` (and no `user-data`), and the
|
||||
/// `ssh` block omits `private-key`, we generate a per-VM Ed25519 keypair: the public key is
|
||||
/// injected into cloud-config and the private key PEM is used for SSH auth.
|
||||
async fn resolve_cloud_init_and_ssh(
|
||||
def: &VmDef,
|
||||
base_dir: &Path,
|
||||
) -> Result<(Option<CloudInitConfig>, Option<SshConfig>)> {
|
||||
let ssh_user = def.ssh.as_ref().map(|s| s.user.as_str()).unwrap_or("vm");
|
||||
let hostname = def
|
||||
.cloud_init
|
||||
.as_ref()
|
||||
.and_then(|ci| ci.hostname.as_deref())
|
||||
.unwrap_or(&def.name);
|
||||
|
||||
// --- Cloud-init: raw user-data file ---
|
||||
if let Some(ci) = &def.cloud_init {
|
||||
if let Some(raw_path) = &ci.user_data {
|
||||
let p = resolve_path(raw_path, base_dir);
|
||||
let data =
|
||||
tokio::fs::read(&p)
|
||||
.await
|
||||
.map_err(|e| VmError::VmFileValidation {
|
||||
vm: def.name.clone(),
|
||||
detail: format!("cannot read user-data at {}: {e}", p.display()),
|
||||
hint: "check the user-data path".into(),
|
||||
})?;
|
||||
let cloud_init = Some(CloudInitConfig {
|
||||
user_data: data,
|
||||
instance_id: Some(def.name.clone()),
|
||||
hostname: ci.hostname.clone().or_else(|| Some(def.name.clone())),
|
||||
});
|
||||
// SSH config from explicit key (if any)
|
||||
let ssh = resolve_ssh_config_from_def(def, base_dir);
|
||||
return Ok((cloud_init, ssh));
|
||||
}
|
||||
}
|
||||
|
||||
// --- Cloud-init: explicit ssh-key file ---
|
||||
if let Some(ci) = &def.cloud_init {
|
||||
if let Some(key_raw) = &ci.ssh_key {
|
||||
let key_path = resolve_path(key_raw, base_dir);
|
||||
let pubkey =
|
||||
tokio::fs::read_to_string(&key_path)
|
||||
.await
|
||||
.map_err(|e| VmError::VmFileValidation {
|
||||
vm: def.name.clone(),
|
||||
detail: format!("cannot read ssh-key at {}: {e}", key_path.display()),
|
||||
hint: "check the ssh-key path".into(),
|
||||
})?;
|
||||
let (user_data, _meta) =
|
||||
build_cloud_config(ssh_user, pubkey.trim(), &def.name, hostname);
|
||||
let cloud_init = Some(CloudInitConfig {
|
||||
user_data,
|
||||
instance_id: Some(def.name.clone()),
|
||||
hostname: Some(hostname.to_string()),
|
||||
});
|
||||
let ssh = resolve_ssh_config_from_def(def, base_dir);
|
||||
return Ok((cloud_init, ssh));
|
||||
}
|
||||
}
|
||||
|
||||
// --- Cloud-init block present but no ssh-key / no user-data → generate keypair ---
|
||||
if def.cloud_init.is_some() {
|
||||
info!(vm = %def.name, "generating Ed25519 SSH keypair for cloud-init");
|
||||
let (pub_openssh, priv_pem) = generate_ssh_keypair(&def.name)?;
|
||||
|
||||
let (user_data, _meta) =
|
||||
build_cloud_config(ssh_user, &pub_openssh, &def.name, hostname);
|
||||
let cloud_init = Some(CloudInitConfig {
|
||||
user_data,
|
||||
instance_id: Some(def.name.clone()),
|
||||
hostname: Some(hostname.to_string()),
|
||||
});
|
||||
let ssh = Some(SshConfig {
|
||||
user: ssh_user.to_string(),
|
||||
public_key: Some(pub_openssh),
|
||||
private_key_path: None,
|
||||
private_key_pem: Some(priv_pem),
|
||||
});
|
||||
return Ok((cloud_init, ssh));
|
||||
}
|
||||
|
||||
// --- No cloud-init at all ---
|
||||
let ssh = resolve_ssh_config_from_def(def, base_dir);
|
||||
Ok((None, ssh))
|
||||
}
|
||||
|
||||
/// Build an `SshConfig` from an explicit `private-key` path in the SSH block.
|
||||
/// Returns `None` if there is no ssh block or no private-key specified.
|
||||
fn resolve_ssh_config_from_def(def: &VmDef, base_dir: &Path) -> Option<SshConfig> {
|
||||
def.ssh.as_ref().and_then(|s| {
|
||||
let key_path = s.private_key.as_ref()?;
|
||||
Some(SshConfig {
|
||||
user: s.user.clone(),
|
||||
public_key: None,
|
||||
private_key_path: Some(resolve_path(key_path, base_dir)),
|
||||
private_key_pem: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -611,7 +684,7 @@ vm "web" {
|
|||
|
||||
let ssh = vm.ssh.as_ref().unwrap();
|
||||
assert_eq!(ssh.user, "admin");
|
||||
assert_eq!(ssh.private_key, "~/.ssh/id_ed25519");
|
||||
assert_eq!(ssh.private_key.as_deref(), Some("~/.ssh/id_ed25519"));
|
||||
|
||||
assert_eq!(vm.provisions.len(), 2);
|
||||
assert!(
|
||||
|
|
|
|||
|
|
@ -88,3 +88,62 @@ fn ssh_port_for_handle(handle: &VmHandle) -> u16 {
|
|||
_ => 22,
|
||||
}
|
||||
}
|
||||
|
||||
/// Well-known filename for a generated SSH private key, stored in the VM's work directory.
|
||||
const GENERATED_KEY_FILE: &str = "id_ed25519_generated";
|
||||
|
||||
/// Persist a generated SSH private key PEM to the VM's work directory (if present).
|
||||
async fn save_generated_ssh_key(
|
||||
spec: &vm_manager::VmSpec,
|
||||
handle: &VmHandle,
|
||||
) -> miette::Result<()> {
|
||||
if let Some(ref ssh) = spec.ssh {
|
||||
if let Some(ref pem) = ssh.private_key_pem {
|
||||
let key_path = handle.work_dir.join(GENERATED_KEY_FILE);
|
||||
tokio::fs::write(&key_path, pem)
|
||||
.await
|
||||
.map_err(|e| miette::miette!("failed to save generated SSH key: {e}"))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build an `SshConfig` from a VMFile ssh block and (optionally) a persisted generated key.
|
||||
///
|
||||
/// If the ssh block specifies `private-key`, use that file. Otherwise, look for a previously
|
||||
/// generated key in the VM's work directory (written during `vmctl up`).
|
||||
fn build_ssh_config(
|
||||
ssh_def: &vm_manager::vmfile::SshDef,
|
||||
base_dir: &std::path::Path,
|
||||
handle: &VmHandle,
|
||||
) -> miette::Result<vm_manager::SshConfig> {
|
||||
if let Some(ref key_path) = ssh_def.private_key {
|
||||
return Ok(vm_manager::SshConfig {
|
||||
user: ssh_def.user.clone(),
|
||||
public_key: None,
|
||||
private_key_path: Some(vm_manager::vmfile::resolve_path(key_path, base_dir)),
|
||||
private_key_pem: None,
|
||||
});
|
||||
}
|
||||
|
||||
// Look for a generated key in the VM's work directory
|
||||
let gen_key_path = handle.work_dir.join(GENERATED_KEY_FILE);
|
||||
if gen_key_path.exists() {
|
||||
let pem = std::fs::read_to_string(&gen_key_path).map_err(|e| {
|
||||
miette::miette!(
|
||||
"cannot read generated SSH key at {}: {e}",
|
||||
gen_key_path.display()
|
||||
)
|
||||
})?;
|
||||
Ok(vm_manager::SshConfig {
|
||||
user: ssh_def.user.clone(),
|
||||
public_key: None,
|
||||
private_key_path: None,
|
||||
private_key_pem: Some(pem),
|
||||
})
|
||||
} else {
|
||||
Err(miette::miette!(
|
||||
"no SSH private-key configured and no generated key found for VM — run `vmctl up` first"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,15 +62,7 @@ pub async fn run(args: ProvisionArgs) -> Result<()> {
|
|||
let ip = hv.guest_ip(handle).await.into_diagnostic()?;
|
||||
let port = super::ssh_port_for_handle(handle);
|
||||
|
||||
let config = vm_manager::SshConfig {
|
||||
user: ssh_def.user.clone(),
|
||||
public_key: None,
|
||||
private_key_path: Some(vm_manager::vmfile::resolve_path(
|
||||
&ssh_def.private_key,
|
||||
&vmfile.base_dir,
|
||||
)),
|
||||
private_key_pem: None,
|
||||
};
|
||||
let config = super::build_ssh_config(ssh_def, &vmfile.base_dir, handle)?;
|
||||
|
||||
println!("Provisioning VM '{}'...", def.name);
|
||||
let sess =
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ pub async fn run(args: ReloadArgs) -> Result<()> {
|
|||
.into_diagnostic()?;
|
||||
|
||||
let handle = hv.prepare(&spec).await.into_diagnostic()?;
|
||||
super::save_generated_ssh_key(&spec, &handle).await?;
|
||||
store.insert(def.name.clone(), handle.clone());
|
||||
state::save_store(&store).await?;
|
||||
|
||||
|
|
@ -98,15 +99,7 @@ async fn run_provision_for_vm(
|
|||
let ip = hv.guest_ip(handle).await.into_diagnostic()?;
|
||||
let port = super::ssh_port_for_handle(handle);
|
||||
|
||||
let config = vm_manager::SshConfig {
|
||||
user: ssh_def.user.clone(),
|
||||
public_key: None,
|
||||
private_key_path: Some(vm_manager::vmfile::resolve_path(
|
||||
&ssh_def.private_key,
|
||||
base_dir,
|
||||
)),
|
||||
private_key_pem: None,
|
||||
};
|
||||
let config = super::build_ssh_config(ssh_def, base_dir, handle)?;
|
||||
|
||||
println!("Provisioning VM '{vm_name}'...");
|
||||
let sess = vm_manager::ssh::connect_with_retry(&ip, port, &config, Duration::from_secs(120))
|
||||
|
|
|
|||
|
|
@ -53,12 +53,18 @@ pub async fn run(args: SshArgs) -> Result<()> {
|
|||
_ => 22,
|
||||
};
|
||||
|
||||
let key_path = args.key.or_else(find_ssh_key).ok_or_else(|| {
|
||||
miette::miette!(
|
||||
"no SSH key found — provide one with --key or ensure ~/.ssh/id_ed25519, \
|
||||
~/.ssh/id_ecdsa, or ~/.ssh/id_rsa exists"
|
||||
)
|
||||
})?;
|
||||
// Check for a generated key in the VM's work directory first, then user keys
|
||||
let generated_key = handle.work_dir.join(super::GENERATED_KEY_FILE);
|
||||
let key_path = args
|
||||
.key
|
||||
.or_else(|| generated_key.exists().then_some(generated_key))
|
||||
.or_else(find_ssh_key)
|
||||
.ok_or_else(|| {
|
||||
miette::miette!(
|
||||
"no SSH key found — provide one with --key or ensure ~/.ssh/id_ed25519, \
|
||||
~/.ssh/id_ecdsa, or ~/.ssh/id_rsa exists"
|
||||
)
|
||||
})?;
|
||||
|
||||
let config = SshConfig {
|
||||
user: args.user.clone(),
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ pub async fn run(args: UpArgs) -> Result<()> {
|
|||
.into_diagnostic()?;
|
||||
|
||||
let handle = hv.prepare(&spec).await.into_diagnostic()?;
|
||||
super::save_generated_ssh_key(&spec, &handle).await?;
|
||||
store.insert(def.name.clone(), handle.clone());
|
||||
state::save_store(&store).await?;
|
||||
|
||||
|
|
@ -119,15 +120,7 @@ async fn run_provision_for_vm(
|
|||
let ip = hv.guest_ip(handle).await.into_diagnostic()?;
|
||||
let port = super::ssh_port_for_handle(handle);
|
||||
|
||||
let config = vm_manager::SshConfig {
|
||||
user: ssh_def.user.clone(),
|
||||
public_key: None,
|
||||
private_key_path: Some(vm_manager::vmfile::resolve_path(
|
||||
&ssh_def.private_key,
|
||||
base_dir,
|
||||
)),
|
||||
private_key_pem: None,
|
||||
};
|
||||
let config = super::build_ssh_config(ssh_def, base_dir, handle)?;
|
||||
|
||||
println!("Provisioning VM '{vm_name}'...");
|
||||
let sess = vm_manager::ssh::connect_with_retry(&ip, port, &config, Duration::from_secs(120))
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue