Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 1 addition & 18 deletions src/components/standalone/security/ips/IpsCreateBypassDrawer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ import { useI18n } from 'vue-i18n'
import { ref, watch } from 'vue'
import { ubusCall, ValidationError } from '@/lib/standalone/ubus'
import { MessageBag } from '@/lib/validation'
import type {
AddressType,
Direction
} from '@/components/standalone/security/ips/IpsFilterBypass.vue'
import type { AddressType } from '@/components/standalone/security/ips/IpsFilterBypass.vue'
import type { RadioOption } from '@nethesis/vue-components'

const { t } = useI18n()
Expand All @@ -31,7 +28,6 @@ watch(
(value) => {
if (value) {
protocol.value = 'ipv4'
direction.value = 'src'
ip.value = ''
description.value = ''
validationErrors.value.clear()
Expand All @@ -44,13 +40,7 @@ const protocolOptions: RadioOption[] = [
{ label: 'IPv6', id: 'ipv6' }
]

const directionOptions: RadioOption[] = [
{ label: t('standalone.ips.source_bypass'), id: 'src' },
{ label: t('standalone.ips.destination_bypass'), id: 'dst' }
]

const protocol = ref<AddressType>('ipv4')
const direction = ref<Direction>('src')
const ip = ref('')
const description = ref('')
const validationErrors = ref(new MessageBag())
Expand All @@ -64,7 +54,6 @@ function save() {
validationErrors.value.clear()
ubusCall('ns.snort', 'create-bypass', {
protocol: protocol.value,
direction: direction.value,
ip: ip.value,
description: description.value
})
Expand Down Expand Up @@ -109,12 +98,6 @@ function closeHandler() {
:invalid-message="t(validationErrors.getFirstI18nKeyFor('ip'))"
:label="t('standalone.ips.ip_address')"
/>
<NeRadioSelection
v-model="direction"
:disabled="loading"
:label="t('standalone.ips.direction')"
:options="directionOptions"
/>
<NeTextInput
v-model="description"
:disabled="loading"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ function deleteBypass() {
loading.value = true
ubusCall('ns.snort', 'delete-bypass', {
protocol: _byPass.value?.protocol,
ip: _byPass.value?.ip,
direction: _byPass.value?.direction
ip: _byPass.value?.ip
})
.then(() => {
emit('deleted')
Expand Down Expand Up @@ -94,11 +93,7 @@ function deleteBypass() {
<template v-else>
{{
t('standalone.ips.delete_bypass_modal_wo_description', {
ip: _byPass?.ip,
direction:
_byPass.direction == 'src'
? t('standalone.ips.source')
: t('standalone.ips.destination')
ip: _byPass?.ip
})
}}
</template>
Expand Down
17 changes: 2 additions & 15 deletions src/components/standalone/security/ips/IpsFilterBypass.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,9 @@ import IpsEnabledBadge from '@/components/standalone/security/ips/IpsEnabledBadg
import IpsDeleteBypassModal from '@/components/standalone/security/ips/IpsDeleteBypassModal.vue'
import type { SortEvent } from '@nethesis/vue-components'

export type Direction = 'src' | 'dst'
export type AddressType = 'ipv4' | 'ipv6'

export type Bypass = {
direction: Direction
protocol: AddressType
ip: string
description: string
Expand Down Expand Up @@ -170,17 +168,14 @@ function handleDeleted() {
<NeTableHeadCell column-key="ip" sortable @sort="onSort">
{{ t('standalone.ips.bypass_address') }}
</NeTableHeadCell>
<NeTableHeadCell column-key="direction" sortable @sort="onSort">
{{ t('standalone.ips.bypass_direction') }}
</NeTableHeadCell>
<NeTableHeadCell>{{ t('standalone.ips.description') }}</NeTableHeadCell>
<NeTableHeadCell>
<!-- no header for actions -->
</NeTableHeadCell>
</NeTableHead>
<NeTableBody>
<NeTableRow v-if="filteredByPasses.length < 1">
<NeTableCell colspan="4">
<NeTableCell colspan="3">
<NeEmptyState
:description="t('common.try_changing_search_filters')"
:icon="faCircleInfo"
Expand All @@ -193,18 +188,10 @@ function handleDeleted() {
</NeEmptyState>
</NeTableCell>
</NeTableRow>
<NeTableRow v-for="item in paginatedItems" v-else :key="`${item.ip}-${item.direction}`">
<NeTableRow v-for="item in paginatedItems" v-else :key="`${item.ip}`">
<NeTableCell :data-label="t('standalone.ips.bypass_address')">
{{ item.ip }}
</NeTableCell>
<NeTableCell :data-label="t('standalone.ips.bypass_direction')">
<template v-if="item.direction == 'src'">
{{ t('standalone.ips.source') }}
</template>
<template v-else>
{{ t('standalone.ips.destination') }}
</template>
</NeTableCell>
<NeTableCell :data-label="t('standalone.ips.description')">
{{ item.description }}
</NeTableCell>
Expand Down
66 changes: 65 additions & 1 deletion src/components/standalone/security/ips/IpsSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

<script lang="ts" setup>
import FormLayout from '@/components/standalone/FormLayout.vue'
import NeMultiTextInput from '@/components/standalone/NeMultiTextInput.vue'
import { useI18n } from 'vue-i18n'
import {
getAxiosErrorMessage,
Expand All @@ -23,13 +24,15 @@ import { faCheck, faFloppyDisk } from '@fortawesome/free-solid-svg-icons'
import { useUciPendingChangesStore } from '@/stores/standalone/uciPendingChanges'
import { onMounted, ref } from 'vue'
import { ubusCall, ValidationError } from '@/lib/standalone/ubus'
import { validateRequired, validateIp4Cidr, type validationOutput } from '@/lib/validation'
import type { AxiosResponse } from 'axios'
import { useIpsStatusStore } from '@/stores/standalone/ipsStatus'

export type IpsSettings = {
enabled: boolean
ns_policy: Policy
oinkcode: string
home_net: string[]
}

export type Policy = 'connectivity' | 'balanced' | 'security' | 'max-detect'
Expand Down Expand Up @@ -66,6 +69,8 @@ const error = ref<Error>()
const enabled = ref(false)
const policy = ref<Policy>('connectivity')
const oinkcode = ref('')
const homeNet = ref<string[]>([''])
const homeNetValidationErrors = ref<string[]>([])

function fetch() {
error.value = undefined
Expand All @@ -74,6 +79,7 @@ function fetch() {
enabled.value = response.data.enabled
policy.value = response.data.ns_policy
oinkcode.value = response.data.oinkcode
homeNet.value = response.data.home_net
})
.catch((reason: Error) => {
error.value = reason
Expand All @@ -90,13 +96,54 @@ onMounted(() => {
const saving = ref(false)
const saveError = ref<Error>()

function validateHomeNet(): boolean {
homeNetValidationErrors.value = []
let isValid = true

// Check if at least one valid entry exists
const nonEmptyEntries = homeNet.value.filter((entry) => entry.trim() !== '')
if (nonEmptyEntries.length === 0) {
homeNetValidationErrors.value = homeNet.value.map(() => t('error.required'))
return false
}

for (const [index, entry] of homeNet.value.entries()) {
const validators: validationOutput[] = []

if (entry.trim() === '') {
validators.push(validateRequired(entry))
} else {
validators.push(validateIp4Cidr(entry))
}

for (const validator of validators) {
if (!validator.valid) {
homeNetValidationErrors.value[index] = t(validator.errMessage as string)
isValid = false
break
}
}

if (homeNetValidationErrors.value[index] === undefined) {
homeNetValidationErrors.value[index] = ''
}
}

return isValid
}

function save() {
if (!validateHomeNet()) {
return
}

saving.value = true
saveError.value = undefined
ubusCall('ns.snort', 'save-settings', {
enabled: enabled.value,
ns_policy: policy.value,
oinkcode: oinkcode.value
oinkcode: oinkcode.value,
home_net: homeNet.value.filter((entry) => entry.trim() !== '')
})
.then(() => {
ips.enabled = enabled.value
Expand Down Expand Up @@ -181,6 +228,22 @@ function checkOinkcode() {
:title="t('standalone.ips.max_detect_info')"
:description="t('standalone.ips.max_detect_description')"
/>
<NeMultiTextInput
v-model="homeNet"
:required="true"
:add-item-label="t('standalone.ips.add_network')"
:title="t('standalone.ips.home_net')"
:invalid-messages="homeNetValidationErrors"
:disabled="saving"
>
<template #tooltip>
<NeTooltip>
<template #content>
{{ t('standalone.ips.home_net_tooltip') }}
</template>
</NeTooltip>
</template>
</NeMultiTextInput>
<div class="space-y-2">
<NeTextInput
v-model="oinkcode"
Expand All @@ -202,6 +265,7 @@ function checkOinkcode() {
</NeTooltip>
</template>
</NeTextInput>

<div class="flex items-center gap-4">
<NeButton
:disabled="checkingOinkcode"
Expand Down
9 changes: 6 additions & 3 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2533,7 +2533,7 @@
"verify_oinkcode": "Test code",
"oinkcode_invalid": "Invalid Oinkcode",
"oinkcode_verified": "Verified",
"filter_bypass_description": "All traffic passing through the firewall is inspected by the IPS. Create a bypass to exclude specific source or destination IP addresses from IPS inspection.",
"filter_bypass_description": "All traffic passing through the firewall is inspected by the IPS. Create a bypass to exclude an IP address from IPS inspection.",
"ips_enabled": "IPS enabled",
"ips_disabled": "IPS disabled",
"no_filter_bypass": "No filter bypass",
Expand All @@ -2553,7 +2553,7 @@
"destination_bypass": "Destination bypass",
"delete_bypass_modal_title": "Delete bypass",
"delete_bypass_modal_w_description": "Delete bypass '{description}'?",
"delete_bypass_modal_wo_description": "Delete bypass with IP address '{ip}' and direction '{direction}'?",
"delete_bypass_modal_wo_description": "Delete bypass for IP address '{ip}'?",
"delete_bypass_error": "Cannot delete bypass",
"disabled_rules_description": "In some scenarios, rules may be too strict or cause too many false positives. To prevent this, you can disable specific rules. Disabled rules are not included in the Snort rule set.",
"error_loading_disabled_rules": "Cannot load disabled rules",
Expand Down Expand Up @@ -2597,7 +2597,10 @@
"destination_suppress_alert": "Suppress alert for destination",
"max_detect_info": "Policy 'max-detect' is enabled.",
"max_detect_description": "Please refer to the manual for more information.",
"events_today": "event today | events today"
"events_today": "event today | events today",
"home_net": "Home networks",
"home_net_tooltip": "Home networks define the protected internal networks. It specifies the IP addresses or subnets Snort should consider as local networks. This helps reduce false positives by distinguishing local traffic from external threats.",
"add_network": "Add network (CIDR notation)"
},
"ha": {
"sidebar_title": "High Availability",
Expand Down
4 changes: 2 additions & 2 deletions src/i18n/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -2469,7 +2469,7 @@
"destination_bypass": "Bypass destinazione",
"suppressed_alerts_description": "Una regola di silenziamento dice all'IPS di ignorare una regola specifica per un indirizzo IP particolare o CIDR, pur valutandola per tutti gli altri indirizzi IP. Questo è utile quando una regola è generalmente efficace, ma genera falsi avvisi positivi per host specifici.",
"security_description": "Massimizza la protezione con un alto numero di regole",
"delete_bypass_modal_wo_description": "Eliminare il bypass con l'indirizzo IP '{ip}' e direzione '{direction}?",
"delete_bypass_modal_wo_description": "Eliminare il bypass per l'indirizzo IP '{ip}'?",
"delete_bypass_modal_title": "Elimina bypass",
"no_rules_found": "Nessuna regola trovata",
"enable_rule_error": "Impossibile abilitare la regola",
Expand All @@ -2480,7 +2480,7 @@
"error_loading_events": "Impossibile caricare gli eventi",
"ips_is_disabled_description": "Attivarlo nella pagina Impostazioni per avviare il monitoraggio di rete",
"add_bypass": "Aggiungi bypass",
"filter_bypass_description": "Tutto il traffico che passa attraverso il firewall è ispezionato dall'IPS. Creare un bypass per escludere dall'IP indirizzi IP specifici per sorgente o destinazione.",
"filter_bypass_description": "Tutto il traffico che passa attraverso il firewall viene ispezionato dall'IPS. Crea un bypass per escludere un indirizzo IP dall'analisi dell'IPS.",
"delete_bypass_modal_w_description": "Eliminare bypass '{description}'?",
"delete_suppressed_alert_modal_title": "Elimina la regola di silenziamento",
"sid_label": "Rule Signature ID (SID)",
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/ta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2538,7 +2538,7 @@
"destination_bypass": "இலக்கு பைபாச்",
"delete_bypass_modal_title": "பைபாசை நீக்கு",
"delete_bypass_modal_w_description": "'{description}' பைபாசை நீக்கவா?",
"delete_bypass_modal_wo_description": "ஐபி முகவரி '{ip}' மற்றும் திசை '{direction}' உடன் பைபாசை நீக்கவா?",
"delete_bypass_modal_wo_description": "ஐபி முகவரி '{ip}' உடன் பைபாசை நீக்கவா?",
"delete_bypass_error": "பைபாசை நீக்க முடியாது",
"disabled_rules_description": "சில சூழ்நிலைகளில், விதிகள் மிகவும் கண்டிப்பானதாக இருக்கலாம் அல்லது பல தவறான நேர்மறைகளை ஏற்படுத்தலாம். இதைத் தடுக்க, நீங்கள் குறிப்பிட்ட விதிகளை முடக்கலாம். முடக்கப்பட்ட விதிகள் Snort விதி தொகுப்பில் சேர்க்கப்படவில்லை.",
"error_loading_disabled_rules": "முடக்கப்பட்ட விதிகளை ஏற்ற முடியாது",
Expand Down
Loading