Auto Create Autorized_Keys
This commit is contained in:
@@ -3,7 +3,10 @@
|
||||
*/
|
||||
import b4a from 'b4a'
|
||||
import bareSsh2 from 'bare-ssh2'
|
||||
import { parseSshdConfig } from './sshd-config-parse.js'
|
||||
import {
|
||||
logicalAuthorizedKeysPath,
|
||||
parseSshdConfig
|
||||
} from './sshd-config-parse.js'
|
||||
import { appendVarLog, BARE_OS_VAR_LOG_DIR } from './bare-os-var-log.js'
|
||||
import { registerBareService } from './bare-initd.js'
|
||||
import { execShellLine, loadBarerc } from './shell.js'
|
||||
@@ -242,10 +245,7 @@ async function loadAuthorizedKeyObjects(ctx, authKeysRel) {
|
||||
const vfs = ctx.vfs
|
||||
const env = vfs?.env
|
||||
const home = String(env?.HOME || '/home/guest')
|
||||
const path =
|
||||
authKeysRel.startsWith('/') || authKeysRel.startsWith('~/')
|
||||
? authKeysRel
|
||||
: `${home.replace(/\/+$/, '')}/${authKeysRel.replace(/^\/+/, '')}`
|
||||
const path = logicalAuthorizedKeysPath(home, authKeysRel)
|
||||
const abs = vfs.resolveLogical(path)
|
||||
let raw
|
||||
try {
|
||||
|
||||
@@ -21,9 +21,76 @@ import {
|
||||
vaultKeyFromSecret
|
||||
} from './identity-account.js'
|
||||
import { bareOsResetShellIdentityState } from './shell.js'
|
||||
import {
|
||||
logicalAuthorizedKeysPath,
|
||||
parseSshdConfig,
|
||||
SSHD_DEFAULT_AUTHORIZED_KEYS_FILE
|
||||
} from './sshd-config-parse.js'
|
||||
|
||||
const LEGACY_ROOT_MIGRATION_STATE = '/.bare-os/migration/legacy-root-v1.json'
|
||||
|
||||
/** @param {string} abs */
|
||||
function posixDirnameForLogicalAbs(abs) {
|
||||
const s = String(abs || '').replace(/\/+$/, '')
|
||||
if (!s || s === '/') return '/'
|
||||
const i = s.lastIndexOf('/')
|
||||
if (i <= 0) return '/'
|
||||
return s.slice(0, i) || '/'
|
||||
}
|
||||
|
||||
/**
|
||||
* Create `$HOME/.ssh` and starter `authorized_keys` when missing (path from `/etc/ssh/sshd_config` when present).
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
async function ensureAuthorizedKeysFile(ctx) {
|
||||
const vfs = ctx.vfs
|
||||
if (
|
||||
!vfs ||
|
||||
typeof vfs.mkdir !== 'function' ||
|
||||
typeof vfs.readFile !== 'function' ||
|
||||
typeof vfs.writeFile !== 'function' ||
|
||||
typeof vfs.resolveLogical !== 'function'
|
||||
)
|
||||
return
|
||||
let rel = SSHD_DEFAULT_AUTHORIZED_KEYS_FILE
|
||||
try {
|
||||
const cfgAbs = vfs.resolveLogical('/etc/ssh/sshd_config')
|
||||
const buf = await vfs.readFile(cfgAbs)
|
||||
if (buf != null && b4a.from(buf).length)
|
||||
rel = parseSshdConfig(b4a.toString(buf)).authorizedKeysFile
|
||||
} catch {
|
||||
/* no readable sshd_config */
|
||||
}
|
||||
const home = String(vfs.env?.HOME || '/home/guest')
|
||||
const logical = logicalAuthorizedKeysPath(home, rel)
|
||||
let abs
|
||||
try {
|
||||
abs = vfs.resolveLogical(logical)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
const parent = posixDirnameForLogicalAbs(abs)
|
||||
try {
|
||||
await vfs.mkdir(parent, { recursive: true })
|
||||
} catch {
|
||||
/* exists */
|
||||
}
|
||||
try {
|
||||
const existing = await vfs.readFile(abs)
|
||||
if (existing != null) return
|
||||
} catch {
|
||||
/* missing */
|
||||
}
|
||||
try {
|
||||
await vfs.writeFile(
|
||||
abs,
|
||||
b4a.from('# Bare OS: add OpenSSH public keys here (one per line).\n')
|
||||
)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mirror vault NDJSON checkpoints into the tamper-evident host audit chain when the booter exposes it.
|
||||
* @param {Record<string, unknown>} ctx
|
||||
@@ -222,6 +289,11 @@ export async function applyGuestEnv(ctx) {
|
||||
} catch (e) {
|
||||
ctx.console?.error?.('[bare-os] loadBarerc: ' + (e?.message || e))
|
||||
}
|
||||
try {
|
||||
await ensureAuthorizedKeysFile(ctx)
|
||||
} catch (e) {
|
||||
ctx.console?.error?.('[bare-os] ensureAuthorizedKeysFile: ' + (e?.message || e))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -271,6 +343,11 @@ export async function applyUnlockedEnv(ctx, publicKey, secretKey) {
|
||||
} catch (e) {
|
||||
ctx.console?.error?.('[bare-os] loadBarerc: ' + (e?.message || e))
|
||||
}
|
||||
try {
|
||||
await ensureAuthorizedKeysFile(ctx)
|
||||
} catch (e) {
|
||||
ctx.console?.error?.('[bare-os] ensureAuthorizedKeysFile: ' + (e?.message || e))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -328,7 +405,14 @@ export async function ensureGuestHome(ctx) {
|
||||
}
|
||||
try {
|
||||
const existing = await drive.get(keepPath)
|
||||
if (existing) return
|
||||
if (existing) {
|
||||
try {
|
||||
await ensureAuthorizedKeysFile(ctx)
|
||||
} catch (e) {
|
||||
ctx.console?.error?.('[bare-os] ensureAuthorizedKeysFile: ' + (e?.message || e))
|
||||
}
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
/* missing */
|
||||
}
|
||||
@@ -337,6 +421,11 @@ export async function ensureGuestHome(ctx) {
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
try {
|
||||
await ensureAuthorizedKeysFile(ctx)
|
||||
} catch (e) {
|
||||
ctx.console?.error?.('[bare-os] ensureAuthorizedKeysFile: ' + (e?.message || e))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,24 @@
|
||||
/**
|
||||
* Minimal sshd_config subset parser (key value pairs and repeats).
|
||||
* Lives under bare-os-booter so Pear bundles it with the booter (not bare-os-openssh).
|
||||
*/
|
||||
|
||||
/** Default `AuthorizedKeysFile` when sshd_config omits it (OpenSSH-style relative → $HOME). */
|
||||
export const SSHD_DEFAULT_AUTHORIZED_KEYS_FILE = '.ssh/authorized_keys'
|
||||
|
||||
/**
|
||||
* Build the logical path bare-openssh uses before `vfs.resolveLogical` (must stay in sync).
|
||||
* @param {string} [home]
|
||||
* @param {string} [authKeysRel]
|
||||
*/
|
||||
export function logicalAuthorizedKeysPath(home, authKeysRel) {
|
||||
const h = String(home || '/home/guest').replace(/\/+$/, '')
|
||||
const rel = String(authKeysRel ?? SSHD_DEFAULT_AUTHORIZED_KEYS_FILE).trim()
|
||||
if (rel.startsWith('/') || rel.startsWith('~/')) return rel
|
||||
return `${h}/${rel.replace(/^\/+/, '')}`
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @returns {{
|
||||
* port: number,
|
||||
@@ -28,7 +46,7 @@ export function parseSshdConfig(text) {
|
||||
allowTcpForwarding: false,
|
||||
maxAuthTries: 6,
|
||||
clientAliveInterval: 0,
|
||||
authorizedKeysFile: '.ssh/authorized_keys',
|
||||
authorizedKeysFile: SSHD_DEFAULT_AUTHORIZED_KEYS_FILE,
|
||||
subsystemSftp: 'internal-sftp'
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user