Fleet PCs on a trusted (vaulted) network can now reach the GE-Enforce client endpoints (manifest, payload, report) without a per-PC token: the auth path accepts a valid geenforce.fetch/report token OR a source IP in the configured allowlist (setting geenforce_allowed_cidrs). Fail-closed; an empty allowlist means the token stays the only path, so existing deployments are unchanged. Rationale: the client token lives in HKLM on every kiosk, so it does not defend against a compromised kiosk anyway - network-perimeter trust is the same practical strength with far less provisioning + no token-rotation churn on a DB wipe. Documented in-UI that this is perimeter trust, not per-device identity. - _ip_allowlisted() (ipaddress, X-Forwarded-For-aware via _client_ip) - /geenforce/config GET/PUT extended with allowedcidrs, server-validated + normalized (bad CIDR -> 400) - new GE-Enforce > Settings tab (GeEnforceSettings.vue) to edit the allowlist in admin, no SQL - 3 regression tests (allow by IP, reject outside list, empty = token required)
110 lines
3.2 KiB
Vue
110 lines
3.2 KiB
Vue
<template>
|
|
<div class="geenforce-settings">
|
|
<h2>Client Access</h2>
|
|
<p class="muted">
|
|
Fleet PCs on a trusted network can reach the GE-Enforce client endpoints
|
|
(manifest, payload, report) without a per-PC token. List the trusted
|
|
networks below. Leave it empty to require a <code>geenforce.fetch</code>
|
|
token instead.
|
|
</p>
|
|
|
|
<label class="field-label" for="cidrs">Allowed networks (CIDR)</label>
|
|
<textarea
|
|
id="cidrs"
|
|
v-model="cidrs"
|
|
class="cidr-input"
|
|
rows="5"
|
|
spellcheck="false"
|
|
placeholder="10.134.48.0/23 10.48.249.0/26"
|
|
></textarea>
|
|
<small class="input-hint">
|
|
One CIDR (or a plain IP) per line, or comma-separated. Example:
|
|
<code>10.134.48.0/23, 10.48.249.0/26</code>. A caller from any of these
|
|
networks may pull manifests + payloads and post reports with no token.
|
|
</small>
|
|
|
|
<div class="actions">
|
|
<button class="btn btn-primary" :disabled="saving" @click="save">
|
|
{{ saving ? 'Saving...' : 'Save' }}
|
|
</button>
|
|
<span v-if="message" :class="['status', ok ? 'ok' : 'err']">{{ message }}</span>
|
|
</div>
|
|
|
|
<div class="note">
|
|
<strong>Note:</strong> this is network-perimeter trust. Any host on a
|
|
listed network is trusted - including a compromised one. It does not
|
|
replace per-device identity if you need tamper-proof report integrity.
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
import api from '@/api'
|
|
|
|
const cidrs = ref('')
|
|
const saving = ref(false)
|
|
const message = ref('')
|
|
const ok = ref(false)
|
|
|
|
function unwrap(response) { return response.data.data }
|
|
|
|
async function load() {
|
|
try {
|
|
const config = unwrap(await api.get('/geenforce/config'))
|
|
cidrs.value = config.allowedcidrs || ''
|
|
} catch (e) {
|
|
message.value = 'Could not load config.'
|
|
ok.value = false
|
|
}
|
|
}
|
|
|
|
async function save() {
|
|
saving.value = true
|
|
message.value = ''
|
|
try {
|
|
const config = unwrap(await api.put('/geenforce/config', { allowedcidrs: cidrs.value }))
|
|
cidrs.value = config.allowedcidrs || '' // server returns the normalized list
|
|
ok.value = true
|
|
message.value = 'Saved.'
|
|
} catch (e) {
|
|
ok.value = false
|
|
message.value = e?.response?.data?.data?.error?.message || 'Save failed.'
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(load)
|
|
</script>
|
|
|
|
<style scoped>
|
|
.geenforce-settings { max-width: 640px; }
|
|
.muted { color: var(--text-light); }
|
|
.field-label { display: block; font-weight: 600; margin: 1rem 0 0.35rem; }
|
|
.cidr-input {
|
|
width: 100%;
|
|
font-family: 'SF Mono', 'Consolas', monospace;
|
|
font-size: 0.9rem;
|
|
padding: 0.6rem 0.7rem;
|
|
border: 1px solid var(--border);
|
|
border-radius: 0.25rem;
|
|
background: var(--bg-card-solid);
|
|
color: var(--text);
|
|
resize: vertical;
|
|
}
|
|
.input-hint { display: block; color: var(--text-light); margin-top: 0.35rem; }
|
|
.actions { display: flex; align-items: center; gap: 0.75rem; margin-top: 1rem; }
|
|
.status.ok { color: var(--success); font-weight: 600; }
|
|
.status.err { color: var(--danger); font-weight: 600; }
|
|
.note {
|
|
margin-top: 1.5rem;
|
|
padding: 0.75rem 1rem;
|
|
border-left: 4px solid var(--warning);
|
|
background: var(--bg-card);
|
|
border-radius: 0.25rem;
|
|
color: var(--text-light);
|
|
font-size: 0.9rem;
|
|
}
|
|
</style>
|