Require signed agent releases (Refs #64)
This commit is contained in:
@@ -20,7 +20,8 @@ function publicAsset(routeId: string | null, path: string, method: string) {
|
||||
'/agent/ec-agent.py',
|
||||
'/agent/ec-agent-update.py',
|
||||
'/agent/install.sh',
|
||||
'/agent/manifest.json'
|
||||
'/agent/manifest.json',
|
||||
'/agent/manifest.json.sig'
|
||||
].includes(path))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,5 +70,5 @@ export function hostApiError(cause: unknown): Response {
|
||||
export function installerCommand(controller: string, token: string): string {
|
||||
const origin = new URL(controller).origin;
|
||||
const quote = (value: string) => "'" + value.replaceAll("'", "'\\''") + "'";
|
||||
return `curl --fail --silent --show-error --proto '=https' ${quote(origin + '/api/agent/install.sh')} | sudo bash -s -- --controller ${quote(origin)} --token ${quote(token)}`;
|
||||
return `sudo bash ./electric-cherry-install.sh --controller ${quote(origin)} --token ${quote(token)}`;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ import { resolve } from 'node:path';
|
||||
|
||||
/** adapter-node copies public assets to build/client; Vite serves static in development. */
|
||||
export async function agentReleaseAsset(
|
||||
file: 'install.sh' | 'ec-agent.py' | 'ec-agent-update.py' | 'manifest.json'
|
||||
file:
|
||||
'install.sh' | 'ec-agent.py' | 'ec-agent-update.py' | 'manifest.json' | 'manifest.json.sig'
|
||||
): Promise<string> {
|
||||
// A prior production build must never shadow a freshly packaged development release.
|
||||
if (process.env.NODE_ENV === 'development')
|
||||
|
||||
@@ -52,6 +52,12 @@
|
||||
<p role="status">
|
||||
{name} created. Run this command with administrator access on that machine.
|
||||
</p>
|
||||
<p>
|
||||
First copy the reviewed installer from your release publisher to
|
||||
<code>electric-cherry-install.sh</code> and provision the publisher's public signing
|
||||
key at <code>/etc/electric-cherry/release-signing.gpg</code>. Obtain both through a
|
||||
trusted channel independent of this controller.
|
||||
</p>
|
||||
<textarea
|
||||
readonly
|
||||
aria-label="Host installation command"
|
||||
|
||||
@@ -1,17 +1,31 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { agentReleaseAsset } from '$lib/server/hosts/releases.js';
|
||||
export async function GET({ params }: { params: { file: string } }) {
|
||||
if (!['ec-agent.py', 'ec-agent-update.py', 'manifest.json'].includes(params.file))
|
||||
if (
|
||||
!['ec-agent.py', 'ec-agent-update.py', 'manifest.json', 'manifest.json.sig'].includes(
|
||||
params.file
|
||||
)
|
||||
)
|
||||
error(404, 'Release asset not found.');
|
||||
return new Response(
|
||||
await agentReleaseAsset(
|
||||
params.file as 'ec-agent.py' | 'ec-agent-update.py' | 'manifest.json'
|
||||
),
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': params.file.endsWith('.json') ? 'application/json' : 'text/plain',
|
||||
'Cache-Control': 'no-cache'
|
||||
}
|
||||
let content: string;
|
||||
try {
|
||||
content = await agentReleaseAsset(
|
||||
params.file as
|
||||
'ec-agent.py' | 'ec-agent-update.py' | 'manifest.json' | 'manifest.json.sig'
|
||||
);
|
||||
} catch (cause) {
|
||||
if ((cause as NodeJS.ErrnoException).code === 'ENOENT')
|
||||
error(404, 'Release asset not found.');
|
||||
throw cause;
|
||||
}
|
||||
return new Response(content, {
|
||||
headers: {
|
||||
'Content-Type': params.file.endsWith('.sig')
|
||||
? 'application/pgp-signature'
|
||||
: params.file.endsWith('.json')
|
||||
? 'application/json'
|
||||
: 'text/plain',
|
||||
'Cache-Control': 'no-cache'
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -608,6 +608,12 @@
|
||||
>
|
||||
</div>
|
||||
{#if enrollment}<p>Expires {$timeFormat.dateTime(enrollment.expiresAt)}</p>
|
||||
<p>
|
||||
Use a reviewed <code>electric-cherry-install.sh</code> from your
|
||||
release publisher and provision its public signing key at
|
||||
<code>/etc/electric-cherry/release-signing.gpg</code>. Obtain both
|
||||
through a trusted channel independent of this controller.
|
||||
</p>
|
||||
<pre>{enrollment.command}</pre>{/if}{:else}<p>
|
||||
Administrators manage enrollment and agent credentials.
|
||||
</p>{/if}
|
||||
|
||||
Reference in New Issue
Block a user