diff --git a/src/components/standalone/dashboard/ThreatShieldIpCard.vue b/src/components/standalone/dashboard/ThreatShieldIpCard.vue index 52bbcb05c..b8b1f9528 100644 --- a/src/components/standalone/dashboard/ThreatShieldIpCard.vue +++ b/src/components/standalone/dashboard/ThreatShieldIpCard.vue @@ -18,12 +18,14 @@ import { faWarning } from '@fortawesome/free-solid-svg-icons' import router from '@/router' import { getStandaloneRoutePrefix } from '@/lib/router' import { useRoute } from 'vue-router' +import { useQuery } from '@tanstack/vue-query' import { DASHBOARD_REFRESH_INTERVAL, useDashboardOverview } from '@/composables/useDashboardOverview' import { useThreatShieldSettings } from '@/composables/useThreatShieldSettings' import { getStatusBadge } from '@/lib/standalone/dashboard' +import { vmQuery } from '@/lib/standalone/victoriaMetrics' const { t } = useI18n() const route = useRoute() @@ -42,20 +44,39 @@ const { error: settingsError } = useThreatShieldSettings({ refetchInterval: DASHBOARD_REFRESH_INTERVAL }) +// the blocked IPs counter is read straight from VictoriaMetrics, since banIP +// exposes it as a metric instead of a ubus counter +const { + data: serviceCounter, + isPending: isCounterPending, + isError: isCounterError, + error: counterError +} = useQuery({ + queryKey: ['dashboard', 'threat-shield-ip-counter'], + queryFn: () => vmQuery('sum(count_over_time(banip_blocked_bytes[1h]))'), + select: (res) => { + const value = res.data?.result?.[0]?.value?.[1] + return value !== undefined ? Math.trunc(Number(value)) : 0 + }, + refetchInterval: DASHBOARD_REFRESH_INTERVAL +}) + const badge = computed(() => getStatusBadge(overview.value?.services.banip)) -const serviceCounter = computed(() => overview.value?.counters.threat_shield_ip) const isLoggingDisabled = computed( () => - !tsSettings.value?.ban_logforwardlan && - !tsSettings.value?.ban_logforwardwan && - !tsSettings.value?.ban_loginput && + !tsSettings.value?.ban_logoutbound && + !tsSettings.value?.ban_loginbound && !tsSettings.value?.ban_logprerouting ) -const isPending = computed(() => isOverviewPending.value || isSettingsPending.value) -const isError = computed(() => isOverviewError.value || isSettingsError.value) -const error = computed(() => overviewError.value ?? settingsError.value) +const isPending = computed( + () => isOverviewPending.value || isSettingsPending.value || isCounterPending.value +) +const isError = computed( + () => isOverviewError.value || isSettingsError.value || isCounterError.value +) +const error = computed(() => overviewError.value ?? settingsError.value ?? counterError.value) const errorTitle = computed(() => (isError.value ? t('error.cannot_retrieve_service_status') : '')) const errorDescription = computed(() => (isError.value ? t(getAxiosErrorMessage(error.value)) : '')) diff --git a/src/components/standalone/security/threat_shield/SettingsTab.vue b/src/components/standalone/security/threat_shield/SettingsTab.vue index a7bcc1a29..4d7e4e0dc 100644 --- a/src/components/standalone/security/threat_shield/SettingsTab.vue +++ b/src/components/standalone/security/threat_shield/SettingsTab.vue @@ -26,6 +26,8 @@ import { onMounted } from 'vue' import { useUciPendingChangesStore } from '@/stores/standalone/uciPendingChanges' import NeMultiTextInput from '../../NeMultiTextInput.vue' import { MessageBag } from '@/lib/validation' +import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' +import { faFloppyDisk } from '@fortawesome/free-solid-svg-icons' const { t } = useI18n() const uciChangesStore = useUciPendingChangesStore() @@ -33,9 +35,8 @@ const queryClient = useQueryClient() const isThreatShieldEnabled = ref(false) const isLogPreroutingEnabled = ref(false) -const isLogInputEnabled = ref(false) -const isLogForwardEnabled = ref(false) -const isLogForwardLanEnabled = ref(false) +const isLogInboundEnabled = ref(false) +const isLogOutboundEnabled = ref(false) const isBlockBruteForceEnabled = ref(false) const banTime = ref('') const maxFailedAccesses = ref('') @@ -78,9 +79,8 @@ type ListSettingsResponse = { data: { ban_icmplimit: number ban_logcount: string - ban_logforwardlan: boolean - ban_logforwardwan: boolean - ban_loginput: boolean + ban_logoutbound: boolean + ban_loginbound: boolean ban_loglimit: boolean ban_logprerouting: boolean ban_logterm: string[] @@ -104,9 +104,8 @@ async function fetchSettings() { const threatShieldConfig = res.data.data isThreatShieldEnabled.value = threatShieldConfig.enabled isLogPreroutingEnabled.value = threatShieldConfig.ban_logprerouting - isLogInputEnabled.value = threatShieldConfig.ban_loginput - isLogForwardEnabled.value = threatShieldConfig.ban_logforwardwan - isLogForwardLanEnabled.value = threatShieldConfig.ban_logforwardlan + isLogInboundEnabled.value = threatShieldConfig.ban_loginbound + isLogOutboundEnabled.value = threatShieldConfig.ban_logoutbound isBlockBruteForceEnabled.value = threatShieldConfig.ban_loglimit banTime.value = threatShieldConfig.ban_nftexpiry maxFailedAccesses.value = threatShieldConfig.ban_logcount.toString() @@ -221,9 +220,8 @@ async function saveSettings() { await ubusCall('ns.threatshield', 'edit-settings', { enabled: isThreatShieldEnabled.value, ban_logprerouting: isLogPreroutingEnabled.value, - ban_loginput: isLogInputEnabled.value, - ban_logforwardwan: isLogForwardEnabled.value, - ban_logforwardlan: isLogForwardLanEnabled.value, + ban_loginbound: isLogInboundEnabled.value, + ban_logoutbound: isLogOutboundEnabled.value, ban_loglimit: isBlockBruteForceEnabled.value, ban_nftexpiry: banTime.value, ban_logcount: Number(maxFailedAccesses.value), @@ -288,12 +286,7 @@ onMounted(() => {