summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/add-pr-labels.yml7
-rw-r--r--.github/workflows/auto-author-assign.yml2
-rw-r--r--.github/workflows/chceck-pr-message.yml4
-rw-r--r--.github/workflows/check-pr-conflicts.yml2
-rw-r--r--.github/workflows/check-stale.yml2
-rw-r--r--.github/workflows/check-unused-imports.yml4
-rw-r--r--.github/workflows/codeql.yml4
-rw-r--r--.github/workflows/label-backport.yml2
-rw-r--r--.github/workflows/linit-j2.yml18
-rw-r--r--.github/workflows/repo-sync.yml6
-rw-r--r--.github/workflows/sonarcloud.yml20
-rw-r--r--Makefile3
-rw-r--r--data/configd-include.json2
-rw-r--r--data/templates/conntrackd/conntrackd.op-mode.j213
-rw-r--r--data/templates/ids/suricata.j21280
-rw-r--r--data/templates/ids/suricata_logrotate.j217
-rw-r--r--data/templates/rsyslog/logrotate.j26
-rw-r--r--data/templates/ssh/sshd_config.j25
-rw-r--r--data/templates/system/40_usb_autosuspend.j25
-rw-r--r--data/templates/telegraf/telegraf.j217
-rw-r--r--debian/control2
-rw-r--r--interface-definitions/container.xml.in29
-rw-r--r--interface-definitions/include/bgp/peer-group.xml.i2
-rw-r--r--interface-definitions/interfaces_geneve.xml.in1
-rw-r--r--interface-definitions/nat_cgnat.xml.in6
-rw-r--r--interface-definitions/service_monitoring_telegraf.xml.in33
-rw-r--r--interface-definitions/service_ssh.xml.in13
-rw-r--r--interface-definitions/service_suricata.xml.in238
-rw-r--r--interface-definitions/system_option.xml.in19
-rw-r--r--interface-definitions/vpn_openconnect.xml.in2
-rw-r--r--op-mode-definitions/dhcp.xml.in39
-rw-r--r--op-mode-definitions/generate-ipsec-profile.xml.in10
-rw-r--r--op-mode-definitions/lldp.xml.in17
-rw-r--r--op-mode-definitions/monitor-log.xml.in41
-rw-r--r--op-mode-definitions/pki.xml.in2
-rw-r--r--op-mode-definitions/restart-ssh.xml.in2
-rw-r--r--op-mode-definitions/show-log.xml.in41
-rw-r--r--op-mode-definitions/suricata.xml.in23
-rw-r--r--op-mode-definitions/traffic-dump.xml.in55
-rw-r--r--python/vyos/config_mgmt.py4
-rw-r--r--python/vyos/configdict.py2
-rw-r--r--python/vyos/configquery.py60
-rw-r--r--python/vyos/configsession.py10
-rw-r--r--python/vyos/defaults.py5
-rw-r--r--python/vyos/ethtool.py4
-rw-r--r--python/vyos/ifconfig/macsec.py2
-rw-r--r--python/vyos/utils/__init__.py4
-rw-r--r--python/vyos/utils/auth.py16
-rw-r--r--python/vyos/utils/configfs.py37
-rw-r--r--python/vyos/utils/cpu.py (renamed from python/vyos/cpu.py)1
-rw-r--r--python/vyos/utils/dict.py7
-rw-r--r--python/vyos/utils/error.py24
-rw-r--r--smoketest/config-tests/container-simple1
-rw-r--r--smoketest/config-tests/dialup-router-wireguard-ipv68
-rw-r--r--smoketest/configs/container-simple5
-rw-r--r--smoketest/scripts/cli/base_accel_ppp_test.py2
-rw-r--r--smoketest/scripts/cli/base_vyostest_shim.py5
-rwxr-xr-xsmoketest/scripts/cli/test_container.py5
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_macsec.py8
-rwxr-xr-xsmoketest/scripts/cli/test_service_monitoring_telegraf.py29
-rwxr-xr-xsmoketest/scripts/cli/test_service_ssh.py16
-rwxr-xr-xsmoketest/scripts/cli/test_system_login.py10
-rwxr-xr-xsrc/completion/list_container_sysctl_parameters.sh20
-rwxr-xr-xsrc/conf_mode/container.py57
-rwxr-xr-xsrc/conf_mode/interfaces_macsec.py10
-rwxr-xr-xsrc/conf_mode/nat_cgnat.py117
-rwxr-xr-xsrc/conf_mode/pki.py2
-rwxr-xr-xsrc/conf_mode/service_monitoring_telegraf.py21
-rwxr-xr-xsrc/conf_mode/service_snmp.py28
-rwxr-xr-xsrc/conf_mode/service_suricata.py161
-rwxr-xr-xsrc/conf_mode/system_login.py50
-rwxr-xr-xsrc/conf_mode/system_option.py18
-rwxr-xr-xsrc/conf_mode/vpn_openconnect.py43
-rw-r--r--src/etc/systemd/system/suricata.service.d/10-override.conf9
-rwxr-xr-xsrc/helpers/vyos-config-encrypt.py5
-rwxr-xr-xsrc/migration-scripts/firewall/15-to-165
-rwxr-xr-xsrc/migration-scripts/firewall/7-to-86
-rwxr-xr-xsrc/migration-scripts/interfaces/20-to-217
-rwxr-xr-xsrc/migration-scripts/nat/6-to-72
-rwxr-xr-xsrc/op_mode/conntrack_sync.py25
-rwxr-xr-xsrc/op_mode/cpu.py12
-rwxr-xr-xsrc/op_mode/dhcp.py10
-rwxr-xr-xsrc/op_mode/ikev2_profile_generator.py57
-rwxr-xr-xsrc/op_mode/lldp.py23
-rwxr-xr-xsrc/op_mode/pki.py22
-rwxr-xr-xsrc/op_mode/powerctrl.py2
-rw-r--r--src/op_mode/show_techsupport_report.py23
-rw-r--r--src/op_mode/tcpdump.py165
-rwxr-xr-xsrc/op_mode/uptime.py4
-rwxr-xr-xsrc/services/vyos-configd10
-rw-r--r--src/shim/vyshim.c11
91 files changed, 1163 insertions, 2021 deletions
diff --git a/.github/workflows/add-pr-labels.yml b/.github/workflows/add-pr-labels.yml
index 1723cceb0..760ddefee 100644
--- a/.github/workflows/add-pr-labels.yml
+++ b/.github/workflows/add-pr-labels.yml
@@ -4,10 +4,7 @@ name: Add pull request labels
on:
pull_request_target:
branches:
- - current
- - crux
- - equuleus
- - sagitta
+ - circinus
permissions:
pull-requests: write
@@ -15,5 +12,5 @@ permissions:
jobs:
add-pr-label:
- uses: vyos/.github/.github/workflows/add-pr-labels.yml@feature/T6349-reusable-workflows
+ uses: vyos/.github/.github/workflows/add-pr-labels.yml@circinus
secrets: inherit
diff --git a/.github/workflows/auto-author-assign.yml b/.github/workflows/auto-author-assign.yml
index c3696ea47..61612cce3 100644
--- a/.github/workflows/auto-author-assign.yml
+++ b/.github/workflows/auto-author-assign.yml
@@ -10,5 +10,5 @@ permissions:
jobs:
assign-author:
- uses: vyos/.github/.github/workflows/assign-author.yml@feature/T6349-reusable-workflows
+ uses: vyos/.github/.github/workflows/assign-author.yml@current
secrets: inherit
diff --git a/.github/workflows/chceck-pr-message.yml b/.github/workflows/chceck-pr-message.yml
index 460662014..c567a5934 100644
--- a/.github/workflows/chceck-pr-message.yml
+++ b/.github/workflows/chceck-pr-message.yml
@@ -7,6 +7,8 @@ on:
- current
- crux
- equuleus
+ - sagitta
+ - circinus
types: [opened, synchronize, edited]
permissions:
@@ -15,5 +17,5 @@ permissions:
jobs:
check-pr-title:
- uses: vyos/.github/.github/workflows/check-pr-message.yml@feature/T6349-reusable-workflows
+ uses: vyos/.github/.github/workflows/check-pr-message.yml@current
secrets: inherit
diff --git a/.github/workflows/check-pr-conflicts.yml b/.github/workflows/check-pr-conflicts.yml
index 0c659e6ed..f09e66415 100644
--- a/.github/workflows/check-pr-conflicts.yml
+++ b/.github/workflows/check-pr-conflicts.yml
@@ -10,5 +10,5 @@ permissions:
jobs:
check-pr-conflict-call:
- uses: vyos/.github/.github/workflows/check-pr-merge-conflict.yml@feature/T6349-reusable-workflows
+ uses: vyos/.github/.github/workflows/check-pr-merge-conflict.yml@current
secrets: inherit
diff --git a/.github/workflows/check-stale.yml b/.github/workflows/check-stale.yml
index b5ec533f1..2adbee2f6 100644
--- a/.github/workflows/check-stale.yml
+++ b/.github/workflows/check-stale.yml
@@ -9,5 +9,5 @@ permissions:
jobs:
stale:
- uses: vyos/.github/.github/workflows/check-stale.yml@feature/T6349-reusable-workflows
+ uses: vyos/.github/.github/workflows/check-stale.yml@current
secrets: inherit
diff --git a/.github/workflows/check-unused-imports.yml b/.github/workflows/check-unused-imports.yml
index aada264f7..322d4f3a8 100644
--- a/.github/workflows/check-unused-imports.yml
+++ b/.github/workflows/check-unused-imports.yml
@@ -3,7 +3,9 @@ on:
pull_request:
branches:
- current
+ - equuleus
- sagitta
+ - circinus
workflow_dispatch:
permissions:
@@ -11,5 +13,5 @@ permissions:
jobs:
check-unused-imports:
- uses: vyos/.github/.github/workflows/check-unused-imports.yml@feature/T6349-reusable-workflows
+ uses: vyos/.github/.github/workflows/check-unused-imports.yml@current
secrets: inherit
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index f6472784d..12654e42e 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -2,7 +2,7 @@ name: "Perform CodeQL Analysis"
on:
push:
- branches: [ "current", "sagitta", "equuleus" ]
+ branches: [ "current", "sagitta", "equuleus", "circinus" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "current" ]
@@ -16,7 +16,7 @@ permissions:
jobs:
codeql-analysis-call:
- uses: vyos/.github/.github/workflows/codeql-analysis.yml@feature/T6349-reusable-workflows
+ uses: vyos/.github/.github/workflows/codeql-analysis.yml@current
secrets: inherit
with:
languages: "['python']"
diff --git a/.github/workflows/label-backport.yml b/.github/workflows/label-backport.yml
index 9192b8184..efbd4388f 100644
--- a/.github/workflows/label-backport.yml
+++ b/.github/workflows/label-backport.yml
@@ -8,5 +8,5 @@ permissions:
jobs:
mergifyio-backport:
- uses: vyos/.github/.github/workflows/label-backport.yml@feature/T6349-reusable-workflows
+ uses: vyos/.github/.github/workflows/label-backport.yml@current
secrets: inherit
diff --git a/.github/workflows/linit-j2.yml b/.github/workflows/linit-j2.yml
deleted file mode 100644
index 364a65a14..000000000
--- a/.github/workflows/linit-j2.yml
+++ /dev/null
@@ -1,18 +0,0 @@
----
-name: J2 Lint
-
-on:
- pull_request:
- branches:
- - current
- - crux
- - equuleus
-
-permissions:
- pull-requests: write
- contents: read
-
-jobs:
- j2lint:
- uses: vyos/.github/.github/workflows/lint-j2.yml@feature/T6349-reusable-workflows
- secrets: inherit
diff --git a/.github/workflows/repo-sync.yml b/.github/workflows/repo-sync.yml
index 36f323cdd..05a0049cf 100644
--- a/.github/workflows/repo-sync.yml
+++ b/.github/workflows/repo-sync.yml
@@ -1,16 +1,16 @@
-name: Repo-sync
+name: Repo-sync circinus
on:
pull_request_target:
types:
- closed
branches:
- - current
+ - circinus
workflow_dispatch:
jobs:
trigger-sync:
- uses: vyos/.github/.github/workflows/trigger-repo-sync.yml@feature/T6349-reusable-workflows
+ uses: vyos/.github/.github/workflows/trigger-repo-sync.yml@circinus
secrets:
REMOTE_REPO: ${{ secrets.REMOTE_REPO }}
REMOTE_OWNER: ${{ secrets.REMOTE_OWNER }}
diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml
new file mode 100644
index 000000000..d59460fe2
--- /dev/null
+++ b/.github/workflows/sonarcloud.yml
@@ -0,0 +1,20 @@
+name: Sonar Checks
+on:
+ push:
+ branches:
+ - circinus-stream
+ pull_request_target:
+ types: [opened, synchronize, reopened]
+jobs:
+ sonar-cloud:
+ name: SonarCloud
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
+ - name: SonarCloud Scan
+ uses: SonarSource/sonarcloud-github-action@master
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
diff --git a/Makefile b/Makefile
index 3b26273d6..cc382e206 100644
--- a/Makefile
+++ b/Makefile
@@ -61,12 +61,13 @@ op_mode_definitions: $(op_xml_obj)
rm -f $(OP_TMPL_DIR)/clear/node.def
rm -f $(OP_TMPL_DIR)/delete/node.def
- # XXX: ping, traceroute and mtr must be able to recursivly call themselves as the
+ # XXX: tcpdump, ping, traceroute and mtr must be able to recursivly call themselves as the
# options are provided from the scripts themselves
ln -s ../node.tag $(OP_TMPL_DIR)/ping/node.tag/node.tag/
ln -s ../node.tag $(OP_TMPL_DIR)/traceroute/node.tag/node.tag/
ln -s ../node.tag $(OP_TMPL_DIR)/mtr/node.tag/node.tag/
ln -s ../node.tag $(OP_TMPL_DIR)/monitor/traceroute/node.tag/node.tag/
+ ln -s ../node.tag $(OP_TMPL_DIR)/monitor/traffic/interface/node.tag/node.tag/
# XXX: test if there are empty node.def files - this is not allowed as these
# could mask help strings or mandatory priority statements
diff --git a/data/configd-include.json b/data/configd-include.json
index dcee50306..420960fe7 100644
--- a/data/configd-include.json
+++ b/data/configd-include.json
@@ -79,6 +79,7 @@
"service_router-advert.py",
"service_salt-minion.py",
"service_sla.py",
+"service_snmp.py",
"service_ssh.py",
"service_tftp-server.py",
"service_webproxy.py",
@@ -92,6 +93,7 @@
"system_ip.py",
"system_ipv6.py",
"system_lcd.py",
+"system_login.py",
"system_login_banner.py",
"system_logs.py",
"system_option.py",
diff --git a/data/templates/conntrackd/conntrackd.op-mode.j2 b/data/templates/conntrackd/conntrackd.op-mode.j2
deleted file mode 100644
index 82f7e2859..000000000
--- a/data/templates/conntrackd/conntrackd.op-mode.j2
+++ /dev/null
@@ -1,13 +0,0 @@
-Source Destination Protocol
-{% for parsed in data if parsed.flow.meta is vyos_defined %}
-{% for key in parsed.flow.meta %}
-{% if key['@direction'] == 'original' %}
-{% set saddr = key.layer3.src | bracketize_ipv6 %}
-{% set sport = key.layer4.sport %}
-{% set daddr = key.layer3.dst | bracketize_ipv6 %}
-{% set dport = key.layer4.dport %}
-{% set protocol = key.layer4['@protoname'] %}
-{{ "%-48s" | format(saddr ~ ':' ~ sport) }} {{ "%-48s" | format(daddr ~ ':' ~ dport) }} {{ protocol }}
-{% endif %}
-{% endfor %}
-{% endfor %}
diff --git a/data/templates/ids/suricata.j2 b/data/templates/ids/suricata.j2
deleted file mode 100644
index 585db93eb..000000000
--- a/data/templates/ids/suricata.j2
+++ /dev/null
@@ -1,1280 +0,0 @@
-%YAML 1.1
----
-
-# Suricata configuration file. In addition to the comments describing all
-# options in this file, full documentation can be found at:
-# https://suricata.readthedocs.io/en/latest/configuration/suricata-yaml.html
-#
-# This configuration file generated by:
-# Suricata 6.0.10
-
-##
-## Step 1: Inform Suricata about your network
-##
-
-vars:
- # more specific is better for alert accuracy and performance
- address-groups:
-{% for (name, value) in suricata['address_group'] %}
- {{ name }}: "[{{ value | join(',') }}]"
-{% endfor %}
-
- port-groups:
-{% for (name, value) in suricata['port_group'] %}
- {{ name }}: "[{{ value | join(',') }}]"
-{% endfor %}
-
-##
-## Step 2: Select outputs to enable
-##
-
-# The default logging directory. Any log or output file will be
-# placed here if it's not specified with a full path name. This can be
-# overridden with the -l command line parameter.
-default-log-dir: /var/log/suricata/
-
-# Configure the type of alert (and other) logging you would like.
-{% if suricata.log is vyos_defined %}
-outputs:
-{% if suricata.log.eve is vyos_defined %}
- # Extensible Event Format (nicknamed EVE) event log in JSON format
- - eve-log:
- enabled: yes
- filetype: {{ suricata.log.eve.filetype }} #regular|syslog|unix_dgram|unix_stream|redis
- filename: {{ suricata.log.eve.filename }}
-
- types:
-{% if suricata.log.eve.type is not vyos_defined or "alert" in suricata.log.eve.type %}
- - alert:
- tagged-packets: yes
-{% endif %}
-{% if "http" in suricata.log.eve.type %}
- - http:
- enabled: yes
- extended: yes
-{% endif %}
-{% if "tls" in suricata.log.eve.type %}
- - tls:
- enabled: yes
- extended: yes # enable this for extended logging information
-{% endif %}
-{% for protocol in suricata.log.eve.type %}
-{% if protocol not in ["alert","http","tls"] %}
- - {{ protocol }}:
- enabled: yes
-{% endif %}
-{% endfor %}
-{% endif %}
-{% endif %}
-
-##
-## Step 3: Configure common capture settings
-##
-## See "Advanced Capture Options" below for more options, including Netmap
-## and PF_RING.
-##
-
-# Linux high speed capture support
-af-packet:
-{% for interface in suricata.interface %}
- - interface: {{ interface }}
- # Default clusterid. AF_PACKET will load balance packets based on flow.
- cluster-id: 99
- # Default AF_PACKET cluster type. AF_PACKET can load balance per flow or per hash.
- # This is only supported for Linux kernel > 3.1
- # possible value are:
- # * cluster_flow: all packets of a given flow are sent to the same socket
- # * cluster_cpu: all packets treated in kernel by a CPU are sent to the same socket
- # * cluster_qm: all packets linked by network card to a RSS queue are sent to the same
- # socket. Requires at least Linux 3.14.
- # * cluster_ebpf: eBPF file load balancing. See doc/userguide/capture-hardware/ebpf-xdp.rst for
- # more info.
- # Recommended modes are cluster_flow on most boxes and cluster_cpu or cluster_qm on system
- # with capture card using RSS (requires cpu affinity tuning and system IRQ tuning)
- cluster-type: cluster_flow
- # In some fragmentation cases, the hash can not be computed. If "defrag" is set
- # to yes, the kernel will do the needed defragmentation before sending the packets.
- defrag: yes
-{% endfor %}
-
-# Cross platform libpcap capture support
-pcap:
-{% for interface in suricata.interface %}
- - interface: {{ interface }}
-{% endfor %}
-
-# Settings for reading pcap files
-pcap-file:
- # Possible values are:
- # - yes: checksum validation is forced
- # - no: checksum validation is disabled
- # - auto: Suricata uses a statistical approach to detect when
- # checksum off-loading is used. (default)
- # Warning: 'checksum-validation' must be set to yes to have checksum tested
- checksum-checks: auto
-
-# See "Advanced Capture Options" below for more options, including Netmap
-# and PF_RING.
-
-
-##
-## Step 4: App Layer Protocol configuration
-##
-
-# Configure the app-layer parsers.
-#
-# The error-policy setting applies to all app-layer parsers. Values can be
-# "drop-flow", "pass-flow", "bypass", "drop-packet", "pass-packet", "reject" or
-# "ignore" (the default).
-#
-# The protocol's section details each protocol.
-#
-# The option "enabled" takes 3 values - "yes", "no", "detection-only".
-# "yes" enables both detection and the parser, "no" disables both, and
-# "detection-only" enables protocol detection only (parser disabled).
-app-layer:
- # error-policy: ignore
- protocols:
- rfb:
- enabled: yes
- detection-ports:
- dp: 5900, 5901, 5902, 5903, 5904, 5905, 5906, 5907, 5908, 5909
- # MQTT, disabled by default.
- mqtt:
- enabled: yes
- # max-msg-length: 1mb
- # subscribe-topic-match-limit: 100
- # unsubscribe-topic-match-limit: 100
- # Maximum number of live MQTT transactions per flow
- # max-tx: 4096
- krb5:
- enabled: yes
- snmp:
- enabled: yes
- ikev2:
- enabled: yes
- tls:
- enabled: yes
- detection-ports:
- dp: 443
-
- # Generate JA3 fingerprint from client hello. If not specified it
- # will be disabled by default, but enabled if rules require it.
- #ja3-fingerprints: auto
-
- # What to do when the encrypted communications start:
- # - default: keep tracking TLS session, check for protocol anomalies,
- # inspect tls_* keywords. Disables inspection of unmodified
- # 'content' signatures.
- # - bypass: stop processing this flow as much as possible. No further
- # TLS parsing and inspection. Offload flow bypass to kernel
- # or hardware if possible.
- # - full: keep tracking and inspection as normal. Unmodified content
- # keyword signatures are inspected as well.
- #
- # For best performance, select 'bypass'.
- #
- #encryption-handling: default
-
- dcerpc:
- enabled: yes
- ftp:
- enabled: yes
- # memcap: 64mb
- rdp:
- enabled: yes
- ssh:
- enabled: yes
- #hassh: yes
- # HTTP2: Experimental HTTP 2 support. Disabled by default.
- http2:
- enabled: no
- # use http keywords on HTTP2 traffic
- http1-rules: no
- smtp:
- enabled: yes
- raw-extraction: no
- # Configure SMTP-MIME Decoder
- mime:
- # Decode MIME messages from SMTP transactions
- # (may be resource intensive)
- # This field supersedes all others because it turns the entire
- # process on or off
- decode-mime: yes
-
- # Decode MIME entity bodies (ie. Base64, quoted-printable, etc.)
- decode-base64: yes
- decode-quoted-printable: yes
-
- # Maximum bytes per header data value stored in the data structure
- # (default is 2000)
- header-value-depth: 2000
-
- # Extract URLs and save in state data structure
- extract-urls: yes
- # Set to yes to compute the md5 of the mail body. You will then
- # be able to journalize it.
- body-md5: no
- # Configure inspected-tracker for file_data keyword
- inspected-tracker:
- content-limit: 100000
- content-inspect-min-size: 32768
- content-inspect-window: 4096
- imap:
- enabled: detection-only
- smb:
- enabled: yes
- detection-ports:
- dp: 139, 445
-
- # Stream reassembly size for SMB streams. By default track it completely.
- #stream-depth: 0
-
- nfs:
- enabled: yes
- tftp:
- enabled: yes
- dns:
- tcp:
- enabled: yes
- detection-ports:
- dp: 53
- udp:
- enabled: yes
- detection-ports:
- dp: 53
- http:
- enabled: yes
- # memcap: Maximum memory capacity for HTTP
- # Default is unlimited, values can be 64mb, e.g.
-
- # default-config: Used when no server-config matches
- # personality: List of personalities used by default
- # request-body-limit: Limit reassembly of request body for inspection
- # by http_client_body & pcre /P option.
- # response-body-limit: Limit reassembly of response body for inspection
- # by file_data, http_server_body & pcre /Q option.
- #
- # For advanced options, see the user guide
-
-
- # server-config: List of server configurations to use if address matches
- # address: List of IP addresses or networks for this block
- # personality: List of personalities used by this block
- #
- # Then, all the fields from default-config can be overloaded
- #
- # Currently Available Personalities:
- # Minimal, Generic, IDS (default), IIS_4_0, IIS_5_0, IIS_5_1, IIS_6_0,
- # IIS_7_0, IIS_7_5, Apache_2
- libhtp:
- default-config:
- personality: IDS
-
- # Can be specified in kb, mb, gb. Just a number indicates
- # it's in bytes.
- request-body-limit: 100kb
- response-body-limit: 100kb
-
- # inspection limits
- request-body-minimal-inspect-size: 32kb
- request-body-inspect-window: 4kb
- response-body-minimal-inspect-size: 40kb
- response-body-inspect-window: 16kb
-
- # response body decompression (0 disables)
- response-body-decompress-layer-limit: 2
-
- # auto will use http-body-inline mode in IPS mode, yes or no set it statically
- http-body-inline: auto
-
- # Decompress SWF files.
- # Two types: 'deflate', 'lzma', 'both' will decompress deflate and lzma
- # compress-depth:
- # Specifies the maximum amount of data to decompress,
- # set 0 for unlimited.
- # decompress-depth:
- # Specifies the maximum amount of decompressed data to obtain,
- # set 0 for unlimited.
- swf-decompression:
- enabled: yes
- type: both
- compress-depth: 100kb
- decompress-depth: 100kb
-
- # Use a random value for inspection sizes around the specified value.
- # This lowers the risk of some evasion techniques but could lead
- # to detection change between runs. It is set to 'yes' by default.
- #randomize-inspection-sizes: yes
- # If "randomize-inspection-sizes" is active, the value of various
- # inspection size will be chosen from the [1 - range%, 1 + range%]
- # range
- # Default value of "randomize-inspection-range" is 10.
- #randomize-inspection-range: 10
-
- # decoding
- double-decode-path: no
- double-decode-query: no
-
- # Can enable LZMA decompression
- #lzma-enabled: false
- # Memory limit usage for LZMA decompression dictionary
- # Data is decompressed until dictionary reaches this size
- #lzma-memlimit: 1mb
- # Maximum decompressed size with a compression ratio
- # above 2048 (only LZMA can reach this ratio, deflate cannot)
- #compression-bomb-limit: 1mb
- # Maximum time spent decompressing a single transaction in usec
- #decompression-time-limit: 100000
-
- server-config:
-
- #- apache:
- # address: [192.168.1.0/24, 127.0.0.0/8, "::1"]
- # personality: Apache_2
- # # Can be specified in kb, mb, gb. Just a number indicates
- # # it's in bytes.
- # request-body-limit: 4096
- # response-body-limit: 4096
- # double-decode-path: no
- # double-decode-query: no
-
- #- iis7:
- # address:
- # - 192.168.0.0/24
- # - 192.168.10.0/24
- # personality: IIS_7_0
- # # Can be specified in kb, mb, gb. Just a number indicates
- # # it's in bytes.
- # request-body-limit: 4096
- # response-body-limit: 4096
- # double-decode-path: no
- # double-decode-query: no
-
- # Note: Modbus probe parser is minimalist due to the limited usage in the field.
- # Only Modbus message length (greater than Modbus header length)
- # and protocol ID (equal to 0) are checked in probing parser
- # It is important to enable detection port and define Modbus port
- # to avoid false positives
- modbus:
- # How many unanswered Modbus requests are considered a flood.
- # If the limit is reached, the app-layer-event:modbus.flooded; will match.
- #request-flood: 500
-
- enabled: no
- detection-ports:
- dp: 502
- # According to MODBUS Messaging on TCP/IP Implementation Guide V1.0b, it
- # is recommended to keep the TCP connection opened with a remote device
- # and not to open and close it for each MODBUS/TCP transaction. In that
- # case, it is important to set the depth of the stream reassembling as
- # unlimited (stream.reassembly.depth: 0)
-
- # Stream reassembly size for modbus. By default track it completely.
- stream-depth: 0
-
- # DNP3
- dnp3:
- enabled: no
- detection-ports:
- dp: 20000
-
- # SCADA EtherNet/IP and CIP protocol support
- enip:
- enabled: no
- detection-ports:
- dp: 44818
- sp: 44818
-
- ntp:
- enabled: yes
-
- dhcp:
- enabled: yes
-
- sip:
- enabled: yes
-
-# Limit for the maximum number of asn1 frames to decode (default 256)
-asn1-max-frames: 256
-
-# Datasets default settings
-# datasets:
-# # Default fallback memcap and hashsize values for datasets in case these
-# # were not explicitly defined.
-# defaults:
-# memcap: 100mb
-# hashsize: 2048
-
-##############################################################################
-##
-## Advanced settings below
-##
-##############################################################################
-
-##
-## Run Options
-##
-
-# Run Suricata with a specific user-id and group-id:
-#run-as:
-# user: suri
-# group: suri
-
-# Some logging modules will use that name in event as identifier. The default
-# value is the hostname
-#sensor-name: suricata
-
-# Default location of the pid file. The pid file is only used in
-# daemon mode (start Suricata with -D). If not running in daemon mode
-# the --pidfile command line option must be used to create a pid file.
-#pid-file: /var/run/suricata.pid
-
-# Daemon working directory
-# Suricata will change directory to this one if provided
-# Default: "/"
-#daemon-directory: "/"
-
-# Umask.
-# Suricata will use this umask if it is provided. By default it will use the
-# umask passed on by the shell.
-#umask: 022
-
-# Suricata core dump configuration. Limits the size of the core dump file to
-# approximately max-dump. The actual core dump size will be a multiple of the
-# page size. Core dumps that would be larger than max-dump are truncated. On
-# Linux, the actual core dump size may be a few pages larger than max-dump.
-# Setting max-dump to 0 disables core dumping.
-# Setting max-dump to 'unlimited' will give the full core dump file.
-# On 32-bit Linux, a max-dump value >= ULONG_MAX may cause the core dump size
-# to be 'unlimited'.
-
-coredump:
- max-dump: unlimited
-
-# If the Suricata box is a router for the sniffed networks, set it to 'router'. If
-# it is a pure sniffing setup, set it to 'sniffer-only'.
-# If set to auto, the variable is internally switched to 'router' in IPS mode
-# and 'sniffer-only' in IDS mode.
-# This feature is currently only used by the reject* keywords.
-host-mode: auto
-
-# Number of packets preallocated per thread. The default is 1024. A higher number
-# will make sure each CPU will be more easily kept busy, but may negatively
-# impact caching.
-#max-pending-packets: 1024
-
-# Runmode the engine should use. Please check --list-runmodes to get the available
-# runmodes for each packet acquisition method. Default depends on selected capture
-# method. 'workers' generally gives best performance.
-#runmode: autofp
-
-# Specifies the kind of flow load balancer used by the flow pinned autofp mode.
-#
-# Supported schedulers are:
-#
-# hash - Flow assigned to threads using the 5-7 tuple hash.
-# ippair - Flow assigned to threads using addresses only.
-#
-#autofp-scheduler: hash
-
-# Preallocated size for each packet. Default is 1514 which is the classical
-# size for pcap on Ethernet. You should adjust this value to the highest
-# packet size (MTU + hardware header) on your system.
-#default-packet-size: 1514
-
-# Unix command socket that can be used to pass commands to Suricata.
-# An external tool can then connect to get information from Suricata
-# or trigger some modifications of the engine. Set enabled to yes
-# to activate the feature. In auto mode, the feature will only be
-# activated in live capture mode. You can use the filename variable to set
-# the file name of the socket.
-unix-command:
- enabled: yes
- filename: /run/suricata/suricata.socket
-
-# Magic file. The extension .mgc is added to the value here.
-#magic-file: /usr/share/file/magic
-#magic-file:
-
-# GeoIP2 database file. Specify path and filename of GeoIP2 database
-# if using rules with "geoip" rule option.
-#geoip-database: /usr/local/share/GeoLite2/GeoLite2-Country.mmdb
-
-legacy:
- uricontent: enabled
-
-##
-## Detection settings
-##
-
-# Set the order of alerts based on actions
-# The default order is pass, drop, reject, alert
-# action-order:
-# - pass
-# - drop
-# - reject
-# - alert
-
-# Define maximum number of possible alerts that can be triggered for the same
-# packet. Default is 15
-#packet-alert-max: 15
-
-# IP Reputation
-#reputation-categories-file: /etc/suricata/iprep/categories.txt
-#default-reputation-path: /etc/suricata/iprep
-#reputation-files:
-# - reputation.list
-
-# When run with the option --engine-analysis, the engine will read each of
-# the parameters below, and print reports for each of the enabled sections
-# and exit. The reports are printed to a file in the default log dir
-# given by the parameter "default-log-dir", with engine reporting
-# subsection below printing reports in its own report file.
-engine-analysis:
- # enables printing reports for fast-pattern for every rule.
- rules-fast-pattern: yes
- # enables printing reports for each rule
- rules: yes
-
-#recursion and match limits for PCRE where supported
-pcre:
- match-limit: 3500
- match-limit-recursion: 1500
-
-##
-## Advanced Traffic Tracking and Reconstruction Settings
-##
-
-# Host specific policies for defragmentation and TCP stream
-# reassembly. The host OS lookup is done using a radix tree, just
-# like a routing table so the most specific entry matches.
-host-os-policy:
- # Make the default policy windows.
- windows: [0.0.0.0/0]
- bsd: []
- bsd-right: []
- old-linux: []
- linux: []
- old-solaris: []
- solaris: []
- hpux10: []
- hpux11: []
- irix: []
- macos: []
- vista: []
- windows2k3: []
-
-# Defrag settings:
-
-# The memcap-policy value can be "drop-flow", "pass-flow", "bypass",
-# "drop-packet", "pass-packet", "reject" or "ignore" (which is the default).
-defrag:
- memcap: 32mb
- # memcap-policy: ignore
- hash-size: 65536
- trackers: 65535 # number of defragmented flows to follow
- max-frags: 65535 # number of fragments to keep (higher than trackers)
- prealloc: yes
- timeout: 60
-
-# Enable defrag per host settings
-# host-config:
-#
-# - dmz:
-# timeout: 30
-# address: [192.168.1.0/24, 127.0.0.0/8, 1.1.1.0/24, 2.2.2.0/24, "1.1.1.1", "2.2.2.2", "::1"]
-#
-# - lan:
-# timeout: 45
-# address:
-# - 192.168.0.0/24
-# - 192.168.10.0/24
-# - 172.16.14.0/24
-
-# Flow settings:
-# By default, the reserved memory (memcap) for flows is 32MB. This is the limit
-# for flow allocation inside the engine. You can change this value to allow
-# more memory usage for flows.
-# The hash-size determines the size of the hash used to identify flows inside
-# the engine, and by default the value is 65536.
-# At startup, the engine can preallocate a number of flows, to get better
-# performance. The number of flows preallocated is 10000 by default.
-# emergency-recovery is the percentage of flows that the engine needs to
-# prune before clearing the emergency state. The emergency state is activated
-# when the memcap limit is reached, allowing new flows to be created, but
-# pruning them with the emergency timeouts (they are defined below).
-# If the memcap is reached, the engine will try to prune flows
-# with the default timeouts. If it doesn't find a flow to prune, it will set
-# the emergency bit and it will try again with more aggressive timeouts.
-# If that doesn't work, then it will try to kill the oldest flows using
-# last time seen flows.
-# The memcap can be specified in kb, mb, gb. Just a number indicates it's
-# in bytes.
-# The memcap-policy can be "drop-flow", "pass-flow", "bypass", "drop-packet",
-# "pass-packet", "reject" or "ignore" (which is the default).
-
-flow:
- memcap: 128mb
- #memcap-policy: ignore
- hash-size: 65536
- prealloc: 10000
- emergency-recovery: 30
- #managers: 1 # default to one flow manager
- #recyclers: 1 # default to one flow recycler thread
-
-# This option controls the use of VLAN ids in the flow (and defrag)
-# hashing. Normally this should be enabled, but in some (broken)
-# setups where both sides of a flow are not tagged with the same VLAN
-# tag, we can ignore the VLAN id's in the flow hashing.
-vlan:
- use-for-tracking: true
-
-# Specific timeouts for flows. Here you can specify the timeouts that the
-# active flows will wait to transit from the current state to another, on each
-# protocol. The value of "new" determines the seconds to wait after a handshake or
-# stream startup before the engine frees the data of that flow it doesn't
-# change the state to established (usually if we don't receive more packets
-# of that flow). The value of "established" is the amount of
-# seconds that the engine will wait to free the flow if that time elapses
-# without receiving new packets or closing the connection. "closed" is the
-# amount of time to wait after a flow is closed (usually zero). "bypassed"
-# timeout controls locally bypassed flows. For these flows we don't do any other
-# tracking. If no packets have been seen after this timeout, the flow is discarded.
-#
-# There's an emergency mode that will become active under attack circumstances,
-# making the engine to check flow status faster. This configuration variables
-# use the prefix "emergency-" and work similar as the normal ones.
-# Some timeouts doesn't apply to all the protocols, like "closed", for udp and
-# icmp.
-
-flow-timeouts:
-
- default:
- new: 30
- established: 300
- closed: 0
- bypassed: 100
- emergency-new: 10
- emergency-established: 100
- emergency-closed: 0
- emergency-bypassed: 50
- tcp:
- new: 60
- established: 600
- closed: 60
- bypassed: 100
- emergency-new: 5
- emergency-established: 100
- emergency-closed: 10
- emergency-bypassed: 50
- udp:
- new: 30
- established: 300
- bypassed: 100
- emergency-new: 10
- emergency-established: 100
- emergency-bypassed: 50
- icmp:
- new: 30
- established: 300
- bypassed: 100
- emergency-new: 10
- emergency-established: 100
- emergency-bypassed: 50
-
-# Stream engine settings. Here the TCP stream tracking and reassembly
-# engine is configured.
-#
-# stream:
-# memcap: 64mb # Can be specified in kb, mb, gb. Just a
-# # number indicates it's in bytes.
-# memcap-policy: ignore # Can be "drop-flow", "pass-flow", "bypass",
-# # "drop-packet", "pass-packet", "reject" or
-# # "ignore" default is "ignore"
-# checksum-validation: yes # To validate the checksum of received
-# # packet. If csum validation is specified as
-# # "yes", then packets with invalid csum values will not
-# # be processed by the engine stream/app layer.
-# # Warning: locally generated traffic can be
-# # generated without checksum due to hardware offload
-# # of checksum. You can control the handling of checksum
-# # on a per-interface basis via the 'checksum-checks'
-# # option
-# prealloc-sessions: 2k # 2k sessions prealloc'd per stream thread
-# midstream: false # don't allow midstream session pickups
-# midstream-policy: ignore # Can be "drop-flow", "pass-flow", "bypass",
-# # "drop-packet", "pass-packet", "reject" or
-# # "ignore" default is "ignore"
-# async-oneside: false # don't enable async stream handling
-# inline: no # stream inline mode
-# drop-invalid: yes # in inline mode, drop packets that are invalid with regards to streaming engine
-# max-synack-queued: 5 # Max different SYN/ACKs to queue
-# bypass: no # Bypass packets when stream.reassembly.depth is reached.
-# # Warning: first side to reach this triggers
-# # the bypass.
-#
-# reassembly:
-# memcap: 256mb # Can be specified in kb, mb, gb. Just a number
-# # indicates it's in bytes.
-# memcap-policy: ignore # Can be "drop-flow", "pass-flow", "bypass",
-# # "drop-packet", "pass-packet", "reject" or
-# # "ignore" default is "ignore"
-# depth: 1mb # Can be specified in kb, mb, gb. Just a number
-# # indicates it's in bytes.
-# toserver-chunk-size: 2560 # inspect raw stream in chunks of at least
-# # this size. Can be specified in kb, mb,
-# # gb. Just a number indicates it's in bytes.
-# toclient-chunk-size: 2560 # inspect raw stream in chunks of at least
-# # this size. Can be specified in kb, mb,
-# # gb. Just a number indicates it's in bytes.
-# randomize-chunk-size: yes # Take a random value for chunk size around the specified value.
-# # This lowers the risk of some evasion techniques but could lead
-# # to detection change between runs. It is set to 'yes' by default.
-# randomize-chunk-range: 10 # If randomize-chunk-size is active, the value of chunk-size is
-# # a random value between (1 - randomize-chunk-range/100)*toserver-chunk-size
-# # and (1 + randomize-chunk-range/100)*toserver-chunk-size and the same
-# # calculation for toclient-chunk-size.
-# # Default value of randomize-chunk-range is 10.
-#
-# raw: yes # 'Raw' reassembly enabled or disabled.
-# # raw is for content inspection by detection
-# # engine.
-#
-# segment-prealloc: 2048 # number of segments preallocated per thread
-#
-# check-overlap-different-data: true|false
-# # check if a segment contains different data
-# # than what we've already seen for that
-# # position in the stream.
-# # This is enabled automatically if inline mode
-# # is used or when stream-event:reassembly_overlap_different_data;
-# # is used in a rule.
-#
-stream:
- memcap: 64mb
- #memcap-policy: ignore
- checksum-validation: yes # reject incorrect csums
- #midstream: false
- #midstream-policy: ignore
- inline: auto # auto will use inline mode in IPS mode, yes or no set it statically
- reassembly:
- memcap: 256mb
- #memcap-policy: ignore
- depth: 1mb # reassemble 1mb into a stream
- toserver-chunk-size: 2560
- toclient-chunk-size: 2560
- randomize-chunk-size: yes
- #randomize-chunk-range: 10
- #raw: yes
- #segment-prealloc: 2048
- #check-overlap-different-data: true
-
-# Host table:
-#
-# Host table is used by the tagging and per host thresholding subsystems.
-#
-host:
- hash-size: 4096
- prealloc: 1000
- memcap: 32mb
-
-# IP Pair table:
-#
-# Used by xbits 'ippair' tracking.
-#
-#ippair:
-# hash-size: 4096
-# prealloc: 1000
-# memcap: 32mb
-
-# Decoder settings
-
-decoder:
- # Teredo decoder is known to not be completely accurate
- # as it will sometimes detect non-teredo as teredo.
- teredo:
- enabled: true
- # ports to look for Teredo. Max 4 ports. If no ports are given, or
- # the value is set to 'any', Teredo detection runs on _all_ UDP packets.
- ports: $TEREDO_PORTS # syntax: '[3544, 1234]' or '3533' or 'any'.
-
- # VXLAN decoder is assigned to up to 4 UDP ports. By default only the
- # IANA assigned port 4789 is enabled.
- vxlan:
- enabled: true
- ports: $VXLAN_PORTS # syntax: '[8472, 4789]' or '4789'.
-
- # VNTag decode support
- vntag:
- enabled: false
-
- # Geneve decoder is assigned to up to 4 UDP ports. By default only the
- # IANA assigned port 6081 is enabled.
- geneve:
- enabled: true
- ports: $GENEVE_PORTS # syntax: '[6081, 1234]' or '6081'.
-
- # maximum number of decoder layers for a packet
- # max-layers: 16
-
-##
-## Performance tuning and profiling
-##
-
-# The detection engine builds internal groups of signatures. The engine
-# allows us to specify the profile to use for them, to manage memory in an
-# efficient way keeping good performance. For the profile keyword you
-# can use the words "low", "medium", "high" or "custom". If you use custom,
-# make sure to define the values in the "custom-values" section.
-# Usually you would prefer medium/high/low.
-#
-# "sgh mpm-context", indicates how the staging should allot mpm contexts for
-# the signature groups. "single" indicates the use of a single context for
-# all the signature group heads. "full" indicates a mpm-context for each
-# group head. "auto" lets the engine decide the distribution of contexts
-# based on the information the engine gathers on the patterns from each
-# group head.
-#
-# The option inspection-recursion-limit is used to limit the recursive calls
-# in the content inspection code. For certain payload-sig combinations, we
-# might end up taking too much time in the content inspection code.
-# If the argument specified is 0, the engine uses an internally defined
-# default limit. When a value is not specified, there are no limits on the recursion.
-detect:
- profile: medium
- custom-values:
- toclient-groups: 3
- toserver-groups: 25
- sgh-mpm-context: auto
- inspection-recursion-limit: 3000
- # If set to yes, the loading of signatures will be made after the capture
- # is started. This will limit the downtime in IPS mode.
- #delayed-detect: yes
-
- prefilter:
- # default prefiltering setting. "mpm" only creates MPM/fast_pattern
- # engines. "auto" also sets up prefilter engines for other keywords.
- # Use --list-keywords=all to see which keywords support prefiltering.
- default: mpm
-
- # the grouping values above control how many groups are created per
- # direction. Port whitelisting forces that port to get its own group.
- # Very common ports will benefit, as well as ports with many expensive
- # rules.
- grouping:
- #tcp-whitelist: 53, 80, 139, 443, 445, 1433, 3306, 3389, 6666, 6667, 8080
- #udp-whitelist: 53, 135, 5060
-
- profiling:
- # Log the rules that made it past the prefilter stage, per packet
- # default is off. The threshold setting determines how many rules
- # must have made it past pre-filter for that rule to trigger the
- # logging.
- #inspect-logging-threshold: 200
- grouping:
- dump-to-disk: false
- include-rules: false # very verbose
- include-mpm-stats: false
-
-# Select the multi pattern algorithm you want to run for scan/search the
-# in the engine.
-#
-# The supported algorithms are:
-# "ac" - Aho-Corasick, default implementation
-# "ac-bs" - Aho-Corasick, reduced memory implementation
-# "ac-ks" - Aho-Corasick, "Ken Steele" variant
-# "hs" - Hyperscan, available when built with Hyperscan support
-#
-# The default mpm-algo value of "auto" will use "hs" if Hyperscan is
-# available, "ac" otherwise.
-#
-# The mpm you choose also decides the distribution of mpm contexts for
-# signature groups, specified by the conf - "detect.sgh-mpm-context".
-# Selecting "ac" as the mpm would require "detect.sgh-mpm-context"
-# to be set to "single", because of ac's memory requirements, unless the
-# ruleset is small enough to fit in memory, in which case one can
-# use "full" with "ac". The rest of the mpms can be run in "full" mode.
-
-mpm-algo: auto
-
-# Select the matching algorithm you want to use for single-pattern searches.
-#
-# Supported algorithms are "bm" (Boyer-Moore) and "hs" (Hyperscan, only
-# available if Suricata has been built with Hyperscan support).
-#
-# The default of "auto" will use "hs" if available, otherwise "bm".
-
-spm-algo: auto
-
-# Suricata is multi-threaded. Here the threading can be influenced.
-threading:
- set-cpu-affinity: no
- # Tune cpu affinity of threads. Each family of threads can be bound
- # to specific CPUs.
- #
- # These 2 apply to the all runmodes:
- # management-cpu-set is used for flow timeout handling, counters
- # worker-cpu-set is used for 'worker' threads
- #
- # Additionally, for autofp these apply:
- # receive-cpu-set is used for capture threads
- # verdict-cpu-set is used for IPS verdict threads
- #
- cpu-affinity:
- - management-cpu-set:
- cpu: [ 0 ] # include only these CPUs in affinity settings
- - receive-cpu-set:
- cpu: [ 0 ] # include only these CPUs in affinity settings
- - worker-cpu-set:
- cpu: [ "all" ]
- mode: "exclusive"
- # Use explicitly 3 threads and don't compute number by using
- # detect-thread-ratio variable:
- # threads: 3
- prio:
- low: [ 0 ]
- medium: [ "1-2" ]
- high: [ 3 ]
- default: "medium"
- #- verdict-cpu-set:
- # cpu: [ 0 ]
- # prio:
- # default: "high"
- #
- # By default Suricata creates one "detect" thread per available CPU/CPU core.
- # This setting allows controlling this behaviour. A ratio setting of 2 will
- # create 2 detect threads for each CPU/CPU core. So for a dual core CPU this
- # will result in 4 detect threads. If values below 1 are used, less threads
- # are created. So on a dual core CPU a setting of 0.5 results in 1 detect
- # thread being created. Regardless of the setting at a minimum 1 detect
- # thread will always be created.
- #
- detect-thread-ratio: 1.0
- #
- # By default, the per-thread stack size is left to its default setting. If
- # the default thread stack size is too small, use the following configuration
- # setting to change the size. Note that if any thread's stack size cannot be
- # set to this value, a fatal error occurs.
- #
- # Generally, the per-thread stack-size should not exceed 8MB.
- #stack-size: 8mb
-
-# Luajit has a strange memory requirement, its 'states' need to be in the
-# first 2G of the process' memory.
-#
-# 'luajit.states' is used to control how many states are preallocated.
-# State use: per detect script: 1 per detect thread. Per output script: 1 per
-# script.
-luajit:
- states: 128
-
-# Profiling settings. Only effective if Suricata has been built with
-# the --enable-profiling configure flag.
-#
-profiling:
- # Run profiling for every X-th packet. The default is 1, which means we
- # profile every packet. If set to 1000, one packet is profiled for every
- # 1000 received.
- #sample-rate: 1000
-
- # rule profiling
- rules:
-
- # Profiling can be disabled here, but it will still have a
- # performance impact if compiled in.
- enabled: yes
- filename: rule_perf.log
- append: yes
-
- # Sort options: ticks, avgticks, checks, matches, maxticks
- # If commented out all the sort options will be used.
- #sort: avgticks
-
- # Limit the number of sids for which stats are shown at exit (per sort).
- limit: 10
-
- # output to json
- json: yes
-
- # per keyword profiling
- keywords:
- enabled: yes
- filename: keyword_perf.log
- append: yes
-
- prefilter:
- enabled: yes
- filename: prefilter_perf.log
- append: yes
-
- # per rulegroup profiling
- rulegroups:
- enabled: yes
- filename: rule_group_perf.log
- append: yes
-
- # packet profiling
- packets:
-
- # Profiling can be disabled here, but it will still have a
- # performance impact if compiled in.
- enabled: yes
- filename: packet_stats.log
- append: yes
-
- # per packet csv output
- csv:
-
- # Output can be disabled here, but it will still have a
- # performance impact if compiled in.
- enabled: no
- filename: packet_stats.csv
-
- # profiling of locking. Only available when Suricata was built with
- # --enable-profiling-locks.
- locks:
- enabled: no
- filename: lock_stats.log
- append: yes
-
- pcap-log:
- enabled: no
- filename: pcaplog_stats.log
- append: yes
-
-##
-## Netfilter integration
-##
-
-# When running in NFQ inline mode, it is possible to use a simulated
-# non-terminal NFQUEUE verdict.
-# This permits sending all needed packet to Suricata via this rule:
-# iptables -I FORWARD -m mark ! --mark $MARK/$MASK -j NFQUEUE
-# And below, you can have your standard filtering ruleset. To activate
-# this mode, you need to set mode to 'repeat'
-# If you want a packet to be sent to another queue after an ACCEPT decision
-# set the mode to 'route' and set next-queue value.
-# On Linux >= 3.1, you can set batchcount to a value > 1 to improve performance
-# by processing several packets before sending a verdict (worker runmode only).
-# On Linux >= 3.6, you can set the fail-open option to yes to have the kernel
-# accept the packet if Suricata is not able to keep pace.
-# bypass mark and mask can be used to implement NFQ bypass. If bypass mark is
-# set then the NFQ bypass is activated. Suricata will set the bypass mark/mask
-# on packet of a flow that need to be bypassed. The Nefilter ruleset has to
-# directly accept all packets of a flow once a packet has been marked.
-nfq:
-# mode: accept
-# repeat-mark: 1
-# repeat-mask: 1
-# bypass-mark: 1
-# bypass-mask: 1
-# route-queue: 2
-# batchcount: 20
-# fail-open: yes
-
-#nflog support
-nflog:
- # netlink multicast group
- # (the same as the iptables --nflog-group param)
- # Group 0 is used by the kernel, so you can't use it
- - group: 2
- # netlink buffer size
- buffer-size: 18432
- # put default value here
- - group: default
- # set number of packets to queue inside kernel
- qthreshold: 1
- # set the delay before flushing packet in the kernel's queue
- qtimeout: 100
- # netlink max buffer size
- max-size: 20000
-
-##
-## Advanced Capture Options
-##
-
-# General settings affecting packet capture
-capture:
- # disable NIC offloading. It's restored when Suricata exits.
- # Enabled by default.
- #disable-offloading: false
- #
- # disable checksum validation. Same as setting '-k none' on the
- # commandline.
- #checksum-validation: none
-
-# Netmap support
-#
-# Netmap operates with NIC directly in driver, so you need FreeBSD 11+ which has
-# built-in Netmap support or compile and install the Netmap module and appropriate
-# NIC driver for your Linux system.
-# To reach maximum throughput disable all receive-, segmentation-,
-# checksum- offloading on your NIC (using ethtool or similar).
-# Disabling TX checksum offloading is *required* for connecting OS endpoint
-# with NIC endpoint.
-# You can find more information at https://github.com/luigirizzo/netmap
-#
-netmap:
- - interface: default
-
-# PF_RING configuration: for use with native PF_RING support
-# for more info see http://www.ntop.org/products/pf_ring/
-pfring:
- - interface: default
- #threads: 2
-
-# For FreeBSD ipfw(8) divert(4) support.
-# Please make sure you have ipfw_load="YES" and ipdivert_load="YES"
-# in /etc/loader.conf or kldload'ing the appropriate kernel modules.
-# Additionally, you need to have an ipfw rule for the engine to see
-# the packets from ipfw. For Example:
-#
-# ipfw add 100 divert 8000 ip from any to any
-#
-# N.B. This example uses "8000" -- this number must mach the values
-# you passed on the command line, i.e., -d 8000
-#
-ipfw:
-
- # Reinject packets at the specified ipfw rule number. This config
- # option is the ipfw rule number AT WHICH rule processing continues
- # in the ipfw processing system after the engine has finished
- # inspecting the packet for acceptance. If no rule number is specified,
- # accepted packets are reinjected at the divert rule which they entered
- # and IPFW rule processing continues. No check is done to verify
- # this will rule makes sense so care must be taken to avoid loops in ipfw.
- #
- ## The following example tells the engine to reinject packets
- # back into the ipfw firewall AT rule number 5500:
- #
- # ipfw-reinjection-rule-number: 5500
-
-
-napatech:
- # When use_all_streams is set to "yes" the initialization code will query
- # the Napatech service for all configured streams and listen on all of them.
- # When set to "no" the streams config array will be used.
- #
- # This option necessitates running the appropriate NTPL commands to create
- # the desired streams prior to running Suricata.
- #use-all-streams: no
-
- # The streams to listen on when auto-config is disabled or when and threading
- # cpu-affinity is disabled. This can be either:
- # an individual stream (e.g. streams: [0])
- # or
- # a range of streams (e.g. streams: ["0-3"])
- #
- streams: ["0-3"]
-
- # Stream stats can be enabled to provide fine grain packet and byte counters
- # for each thread/stream that is configured.
- #
- enable-stream-stats: no
-
- # When auto-config is enabled the streams will be created and assigned
- # automatically to the NUMA node where the thread resides. If cpu-affinity
- # is enabled in the threading section. Then the streams will be created
- # according to the number of worker threads specified in the worker-cpu-set.
- # Otherwise, the streams array is used to define the streams.
- #
- # This option is intended primarily to support legacy configurations.
- #
- # This option cannot be used simultaneously with either "use-all-streams"
- # or "hardware-bypass".
- #
- auto-config: yes
-
- # Enable hardware level flow bypass.
- #
- hardware-bypass: yes
-
- # Enable inline operation. When enabled traffic arriving on a given port is
- # automatically forwarded out its peer port after analysis by Suricata.
- #
- inline: no
-
- # Ports indicates which Napatech ports are to be used in auto-config mode.
- # these are the port IDs of the ports that will be merged prior to the
- # traffic being distributed to the streams.
- #
- # When hardware-bypass is enabled the ports must be configured as a segment.
- # specify the port(s) on which upstream and downstream traffic will arrive.
- # This information is necessary for the hardware to properly process flows.
- #
- # When using a tap configuration one of the ports will receive inbound traffic
- # for the network and the other will receive outbound traffic. The two ports on a
- # given segment must reside on the same network adapter.
- #
- # When using a SPAN-port configuration the upstream and downstream traffic
- # arrives on a single port. This is configured by setting the two sides of the
- # segment to reference the same port. (e.g. 0-0 to configure a SPAN port on
- # port 0).
- #
- # port segments are specified in the form:
- # ports: [0-1,2-3,4-5,6-6,7-7]
- #
- # For legacy systems when hardware-bypass is disabled this can be specified in any
- # of the following ways:
- #
- # a list of individual ports (e.g. ports: [0,1,2,3])
- #
- # a range of ports (e.g. ports: [0-3])
- #
- # "all" to indicate that all ports are to be merged together
- # (e.g. ports: [all])
- #
- # This parameter has no effect if auto-config is disabled.
- #
- ports: [0-1,2-3]
-
- # When auto-config is enabled the hashmode specifies the algorithm for
- # determining to which stream a given packet is to be delivered.
- # This can be any valid Napatech NTPL hashmode command.
- #
- # The most common hashmode commands are: hash2tuple, hash2tuplesorted,
- # hash5tuple, hash5tuplesorted and roundrobin.
- #
- # See Napatech NTPL documentation other hashmodes and details on their use.
- #
- # This parameter has no effect if auto-config is disabled.
- #
- hashmode: hash5tuplesorted
-
-##
-## Configure Suricata to load Suricata-Update managed rules.
-##
-
-# As VyOS leverages suricata-update, the default rule path points to the
-# generated rules instead of the built-in rules.
-#
-# default-rule-path: /etc/suricata/rules
-default-rule-path: /var/lib/suricata/rules
-
-rule-files:
- - suricata.rules
-
-##
-## Auxiliary configuration files.
-##
-
-# As VyOS leverages suricata-update, the classification file points to the
-# generated classification instead of the built-in one.
-#
-# classification-file: /etc/suricata/classification.config
-classification-file: /var/lib/suricata/rules/classification.config
-reference-config-file: /etc/suricata/reference.config
-# threshold-file: /etc/suricata/threshold.config
-
-##
-## Include other configs
-##
-
-# Includes: Files included here will be handled as if they were in-lined
-# in this configuration file. Files with relative pathnames will be
-# searched for in the same directory as this configuration file. You may
-# use absolute pathnames too.
-# You can specify more than 2 configuration files, if needed.
-#include: include1.yaml
-#include: include2.yaml
diff --git a/data/templates/ids/suricata_logrotate.j2 b/data/templates/ids/suricata_logrotate.j2
deleted file mode 100644
index 62773fc68..000000000
--- a/data/templates/ids/suricata_logrotate.j2
+++ /dev/null
@@ -1,17 +0,0 @@
-{% for filename in [(log.eve.filename | default("eve.json"))] %}
-{{ filename if filename.startswith("/") else ("/var/log/suricata/" + filename) }}
-{% endfor %}{
- weekly
- dateext
- dateformat _%Y-%m-%d_%H-%M-%S
- maxsize 10M
- rotate 10
- missingok
- nocompress
- nocreate
- nomail
- sharedscripts
- postrotate
- /bin/kill -HUP `cat /run/suricata/suricata.pid 2>/dev/null` 2>/dev/null || true
- endscript
-}
diff --git a/data/templates/rsyslog/logrotate.j2 b/data/templates/rsyslog/logrotate.j2
index ea33fea4f..b9689a1cf 100644
--- a/data/templates/rsyslog/logrotate.j2
+++ b/data/templates/rsyslog/logrotate.j2
@@ -5,9 +5,6 @@
create
rotate 5
size=256k
- postrotate
- invoke-rc.d rsyslog rotate > /dev/null
- endscript
}
{% if file is vyos_defined %}
@@ -18,9 +15,6 @@
create
rotate {{ file_options.archive.file }}
size={{ file_options.archive.size | int // 1024 }}k
- postrotate
- invoke-rc.d rsyslog rotate > /dev/null
- endscript
}
{% endfor %}
diff --git a/data/templates/ssh/sshd_config.j2 b/data/templates/ssh/sshd_config.j2
index 650fd25e6..2cf0494c4 100644
--- a/data/templates/ssh/sshd_config.j2
+++ b/data/templates/ssh/sshd_config.j2
@@ -67,6 +67,11 @@ Ciphers {{ ciphers | join(',') }}
HostKeyAlgorithms {{ hostkey_algorithm | join(',') }}
{% endif %}
+{% if pubkey_accepted_algorithm is vyos_defined %}
+# Specifies the available PubKey signature algorithms
+PubkeyAcceptedAlgorithms {{ pubkey_accepted_algorithm | join(',') }}
+{% endif %}
+
{% if mac is vyos_defined %}
# Specifies the available MAC (message authentication code) algorithms
MACs {{ mac | join(',') }}
diff --git a/data/templates/system/40_usb_autosuspend.j2 b/data/templates/system/40_usb_autosuspend.j2
new file mode 100644
index 000000000..01ba86420
--- /dev/null
+++ b/data/templates/system/40_usb_autosuspend.j2
@@ -0,0 +1,5 @@
+{% set autosuspend = "auto" %}
+{% if disable_usb_autosuspend is vyos_defined %}
+{% set autosuspend = "on" %}
+{% endif %}
+ACTION=="add", SUBSYSTEM=="usb", TEST=="power/control", ATTR{power/control}="{{ autosuspend }}"
diff --git a/data/templates/telegraf/telegraf.j2 b/data/templates/telegraf/telegraf.j2
index 9623bdec6..535e3a347 100644
--- a/data/templates/telegraf/telegraf.j2
+++ b/data/templates/telegraf/telegraf.j2
@@ -41,6 +41,21 @@
bucket = "{{ influxdb.bucket }}"
### End InfluxDB2 ###
{% endif %}
+{% if loki is vyos_defined %}
+### Loki ###
+[[outputs.loki]]
+ ## The domain of Loki
+ domain = "{{ loki.url }}:{{ loki.port }}"
+{% if loki.authentication.username is vyos_defined and loki.authentication.password is vyos_defined %}
+ ## Basic Authentication
+ username = "{{ loki.authentication.username }}"
+ password = "{{ loki.authentication.password }}"
+{% endif %}
+{% if loki.metric_name_label is vyos_defined %}
+metric_name_label = "{{ loki.metric_name_label }}"
+{% endif %}
+### End Loki ###
+{% endif %}
{% if prometheus_client is vyos_defined %}
### Prometheus ###
[[outputs.prometheus_client]]
@@ -115,7 +130,9 @@
{% if influxdb is vyos_defined %}
[[inputs.exec]]
commands = [
+{% if nft_chains is vyos_defined %}
"{{ custom_scripts_dir }}/show_firewall_input_filter.py",
+{% endif %}
"{{ custom_scripts_dir }}/show_interfaces_input_filter.py",
"{{ custom_scripts_dir }}/vyos_services_input_filter.py"
]
diff --git a/debian/control b/debian/control
index 2e99bdc28..329ed09eb 100644
--- a/debian/control
+++ b/debian/control
@@ -164,8 +164,6 @@ Depends:
# End "service dns dynamic"
# # For "service ids"
fastnetmon [amd64],
- suricata,
- suricata-update,
# End "service ids"
# # For "service ndp-proxy"
ndppd,
diff --git a/interface-definitions/container.xml.in b/interface-definitions/container.xml.in
index 1ad7215e5..6ea44a6d4 100644
--- a/interface-definitions/container.xml.in
+++ b/interface-definitions/container.xml.in
@@ -71,6 +71,35 @@
<multi/>
</properties>
</leafNode>
+ <node name="sysctl">
+ <properties>
+ <help>Configure namespaced kernel parameters of the container</help>
+ </properties>
+ <children>
+ <tagNode name="parameter">
+ <properties>
+ <help>Sysctl key name</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_container_sysctl_parameters.sh</script>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Sysctl key name</description>
+ </valueHelp>
+ <constraint>
+ <validator name="sysctl"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="value">
+ <properties>
+ <help>Sysctl configuration value</help>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
#include <include/generic-description.xml.i>
<tagNode name="device">
<properties>
diff --git a/interface-definitions/include/bgp/peer-group.xml.i b/interface-definitions/include/bgp/peer-group.xml.i
index 3866fc017..c80d4a394 100644
--- a/interface-definitions/include/bgp/peer-group.xml.i
+++ b/interface-definitions/include/bgp/peer-group.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Peer group for this peer</help>
<completionHelp>
- <path>protocols bgp peer-group</path>
+ <path>${COMP_WORDS[@]:1:${#COMP_WORDS[@]}-5} peer-group</path>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/interfaces_geneve.xml.in b/interface-definitions/interfaces_geneve.xml.in
index c94113271..990c5bd91 100644
--- a/interface-definitions/interfaces_geneve.xml.in
+++ b/interface-definitions/interfaces_geneve.xml.in
@@ -52,6 +52,7 @@
#include <include/interface/mirror.xml.i>
#include <include/interface/redirect.xml.i>
#include <include/interface/tunnel-remote.xml.i>
+ #include <include/interface/vrf.xml.i>
#include <include/vni.xml.i>
</children>
</tagNode>
diff --git a/interface-definitions/nat_cgnat.xml.in b/interface-definitions/nat_cgnat.xml.in
index fce5e655d..71f4d67b0 100644
--- a/interface-definitions/nat_cgnat.xml.in
+++ b/interface-definitions/nat_cgnat.xml.in
@@ -8,6 +8,12 @@
<priority>221</priority>
</properties>
<children>
+ <leafNode name="log-allocation">
+ <properties>
+ <help>Log IP address and port allocation</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<node name="pool">
<properties>
<help>External and internal pool parameters</help>
diff --git a/interface-definitions/service_monitoring_telegraf.xml.in b/interface-definitions/service_monitoring_telegraf.xml.in
index 2624023ea..2ac0d940e 100644
--- a/interface-definitions/service_monitoring_telegraf.xml.in
+++ b/interface-definitions/service_monitoring_telegraf.xml.in
@@ -148,6 +148,39 @@
#include <include/url-http-https.xml.i>
</children>
</node>
+ <node name="loki">
+ <properties>
+ <help>Output plugin Loki</help>
+ </properties>
+ <children>
+ <node name="authentication">
+ <properties>
+ <help>HTTP basic authentication parameters</help>
+ </properties>
+ <children>
+ #include <include/generic-username.xml.i>
+ #include <include/generic-password.xml.i>
+ </children>
+ </node>
+ <leafNode name="metric-name-label">
+ <properties>
+ <help>Metric name label</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Label to use for the metric name</description>
+ </valueHelp>
+ <constraint>
+ #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/port-number.xml.i>
+ <leafNode name="port">
+ <defaultValue>3100</defaultValue>
+ </leafNode>
+ #include <include/url-http-https.xml.i>
+ </children>
+ </node>
<leafNode name="source">
<properties>
<help>Source parameters for monitoring</help>
diff --git a/interface-definitions/service_ssh.xml.in b/interface-definitions/service_ssh.xml.in
index d9eee1ab8..221e451d1 100644
--- a/interface-definitions/service_ssh.xml.in
+++ b/interface-definitions/service_ssh.xml.in
@@ -146,6 +146,19 @@
</constraint>
</properties>
</leafNode>
+ <leafNode name="pubkey-accepted-algorithm">
+ <properties>
+ <help>Allowed pubkey signature algorithms</help>
+ <completionHelp>
+ <!-- generated by ssh -Q PubkeyAcceptedAlgorithms | tr '\n' ' ' as this will not change dynamically -->
+ <list>ssh-ed25519 ssh-ed25519-cert-v01@openssh.com sk-ssh-ed25519@openssh.com sk-ssh-ed25519-cert-v01@openssh.com ecdsa-sha2-nistp256 ecdsa-sha2-nistp256-cert-v01@openssh.com ecdsa-sha2-nistp384 ecdsa-sha2-nistp384-cert-v01@openssh.com ecdsa-sha2-nistp521 ecdsa-sha2-nistp521-cert-v01@openssh.com sk-ecdsa-sha2-nistp256@openssh.com sk-ecdsa-sha2-nistp256-cert-v01@openssh.com webauthn-sk-ecdsa-sha2-nistp256@openssh.com ssh-dss ssh-dss-cert-v01@openssh.com ssh-rsa ssh-rsa-cert-v01@openssh.com rsa-sha2-256 rsa-sha2-256-cert-v01@openssh.com rsa-sha2-512 rsa-sha2-512-cert-v01@openssh.com</list>
+ </completionHelp>
+ <multi/>
+ <constraint>
+ <regex>(ssh-ed25519|ssh-ed25519-cert-v01@openssh.com|sk-ssh-ed25519@openssh.com|sk-ssh-ed25519-cert-v01@openssh.com|ecdsa-sha2-nistp256|ecdsa-sha2-nistp256-cert-v01@openssh.com|ecdsa-sha2-nistp384|ecdsa-sha2-nistp384-cert-v01@openssh.com|ecdsa-sha2-nistp521|ecdsa-sha2-nistp521-cert-v01@openssh.com|sk-ecdsa-sha2-nistp256@openssh.com|sk-ecdsa-sha2-nistp256-cert-v01@openssh.com|webauthn-sk-ecdsa-sha2-nistp256@openssh.com|ssh-dss|ssh-dss-cert-v01@openssh.com|ssh-rsa|ssh-rsa-cert-v01@openssh.com|rsa-sha2-256|rsa-sha2-256-cert-v01@openssh.com|rsa-sha2-512|rsa-sha2-512-cert-v01@openssh.com)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
<leafNode name="key-exchange">
<properties>
<help>Allowed key exchange (KEX) algorithms</help>
diff --git a/interface-definitions/service_suricata.xml.in b/interface-definitions/service_suricata.xml.in
deleted file mode 100644
index e0159e2ba..000000000
--- a/interface-definitions/service_suricata.xml.in
+++ /dev/null
@@ -1,238 +0,0 @@
-<?xml version="1.0"?>
-<interfaceDefinition>
- <node name="service">
- <children>
- <node name="suricata" owner="${vyos_conf_scripts_dir}/service_suricata.py">
- <properties>
- <help>Network IDS, IPS and Security Monitoring</help>
- <priority>740</priority>
- </properties>
- <children>
- #include <include/generic-interface-multi.xml.i>
- <tagNode name="address-group">
- <properties>
- <help>Address group name</help>
- <constraint>
- <regex>[a-z0-9-]+</regex>
- </constraint>
- </properties>
- <children>
- <leafNode name="address">
- <properties>
- <help>IP address or subnet</help>
- <valueHelp>
- <format>ipv4</format>
- <description>IPv4 address to match</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>IPv6 address to match</description>
- </valueHelp>
- <valueHelp>
- <format>ipv4net</format>
- <description>IPv4 prefix to match</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6net</format>
- <description>IPv6 prefix to match</description>
- </valueHelp>
- <valueHelp>
- <format>!ipv4</format>
- <description>Exclude the specified IPv4 address from matches</description>
- </valueHelp>
- <valueHelp>
- <format>!ipv6</format>
- <description>Exclude the specified IPv6 address from matches</description>
- </valueHelp>
- <valueHelp>
- <format>!ipv4net</format>
- <description>Exclude the specified IPv6 prefix from matches</description>
- </valueHelp>
- <valueHelp>
- <format>!ipv6net</format>
- <description>Exclude the specified IPv6 prefix from matches</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-address"/>
- <validator name="ipv6-address"/>
- <validator name="ipv4-prefix"/>
- <validator name="ipv6-prefix"/>
- <validator name="ipv4-address-exclude"/>
- <validator name="ipv6-address-exclude"/>
- <validator name="ipv4-prefix-exclude"/>
- <validator name="ipv6-prefix-exclude"/>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
- <leafNode name="group">
- <properties>
- <help>Address group</help>
- <completionHelp>
- <path>service ids suricata address-group</path>
- </completionHelp>
- <valueHelp>
- <format>txt</format>
- <description>Address group to match</description>
- </valueHelp>
- <valueHelp>
- <format>!txt</format>
- <description>Exclude the specified address group from matches</description>
- </valueHelp>
- <constraint>
- <regex>!?[a-z0-9-]+</regex>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
- </children>
- </tagNode>
- <tagNode name="port-group">
- <properties>
- <help>Port group name</help>
- <constraint>
- <regex>[a-z0-9-]+</regex>
- </constraint>
- </properties>
- <children>
- <leafNode name="port">
- <properties>
- <help>Port number</help>
- <valueHelp>
- <format>u32:1-65535</format>
- <description>Numeric port to match</description>
- </valueHelp>
- <valueHelp>
- <format>!u32:1-65535</format>
- <description>Numeric port to exclude from matches</description>
- </valueHelp>
- <valueHelp>
- <format>start-end</format>
- <description>Numbered port range (e.g. 1001-1005) to match</description>
- </valueHelp>
- <valueHelp>
- <format>!start-end</format>
- <description>Numbered port range (e.g. !1001-1005) to exclude from matches</description>
- </valueHelp>
- <constraint>
- <validator name="port-range"/>
- <validator name="port-range-exclude"/>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
- <leafNode name="group">
- <properties>
- <help>Port group</help>
- <completionHelp>
- <path>service ids suricata port-group</path>
- </completionHelp>
- <valueHelp>
- <format>txt</format>
- <description>Port group to match</description>
- </valueHelp>
- <valueHelp>
- <format>!txt</format>
- <description>Exclude the specified port group from matches</description>
- </valueHelp>
- <constraint>
- <regex>!?[a-z0-9-]+</regex>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
- </children>
- </tagNode>
- <node name="log">
- <properties>
- <help>Suricata log outputs</help>
- </properties>
- <children>
- <node name="eve">
- <properties>
- <help>Extensible Event Format (EVE)</help>
- </properties>
- <children>
- <leafNode name="filetype">
- <properties>
- <help>EVE logging destination</help>
- <completionHelp>
- <list>regular syslog</list>
- </completionHelp>
- <valueHelp>
- <format>regular</format>
- <description>Log to filename</description>
- </valueHelp>
- <valueHelp>
- <format>syslog</format>
- <description>Log to syslog</description>
- </valueHelp>
- <constraint>
- <regex>(regular|syslog)</regex>
- </constraint>
- </properties>
- <defaultValue>regular</defaultValue>
- </leafNode>
- <leafNode name="filename">
- <properties>
- <help>Log file</help>
- <valueHelp>
- <format>filename</format>
- <description>File name in default Suricata log directory</description>
- </valueHelp>
- <valueHelp>
- <format>/path</format>
- <description>Absolute file path</description>
- </valueHelp>
- </properties>
- <defaultValue>eve.json</defaultValue>
- </leafNode>
- <leafNode name="type">
- <properties>
- <help>Log types</help>
- <completionHelp>
- <list>alert anomaly drop files http dns tls smtp dnp3 ftp rdp nfs smb tftp ikev2 dcerpc krb5 snmp rfb sip dhcp ssh mqtt http2 flow netflow</list>
- </completionHelp>
- <valueHelp>
- <format>alert</format>
- <description>Record events for rule matches</description>
- </valueHelp>
- <valueHelp>
- <format>anomaly</format>
- <description>Record unexpected conditions such as truncated packets, packets with invalid IP/UDP/TCP length values, and other events that render the packet invalid for further processing or describe unexpected behavior on an established stream</description>
- </valueHelp>
- <valueHelp>
- <format>drop</format>
- <description>Record events for dropped packets</description>
- </valueHelp>
- <valueHelp>
- <format>file</format>
- <description>Record file details (e.g., MD5) for files extracted from application protocols (e.g., HTTP)</description>
- </valueHelp>
- <valueHelp>
- <format>application (http, dns, tls, ...)</format>
- <description>Record application-level transactions</description>
- </valueHelp>
- <valueHelp>
- <format>flow</format>
- <description>Record bi-directional flows</description>
- </valueHelp>
- <valueHelp>
- <format>netflow</format>
- <description>Record uni-directional flows</description>
- </valueHelp>
- <constraint>
- <regex>(alert|anomaly|http|dns|tls|files|drop|smtp|dnp3|ftp|rdp|nfs|smb|tftp|ikev2|dcerpc|krb5|snmp|rfb|sip|dhcp|ssh|mqtt|http2|flow|netflow)</regex>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
- </children>
- </node>
- </children>
- </node>
- </children>
- </node>
- </children>
- </node>
-</interfaceDefinition>
diff --git a/interface-definitions/system_option.xml.in b/interface-definitions/system_option.xml.in
index fe517d17d..e78a53552 100644
--- a/interface-definitions/system_option.xml.in
+++ b/interface-definitions/system_option.xml.in
@@ -49,6 +49,19 @@
<valueless/>
</properties>
</leafNode>
+ <node name="debug">
+ <properties>
+ <help>Dynamic debugging for kernel module</help>
+ </properties>
+ <children>
+ <leafNode name="wireguard">
+ <properties>
+ <help>Dynamic debugging for Wireguard module</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
</children>
</node>
<leafNode name="keyboard-layout">
@@ -183,6 +196,12 @@
</properties>
<defaultValue>12-hour</defaultValue>
</leafNode>
+ <leafNode name="disable-usb-autosuspend">
+ <properties>
+ <help>Disable autosuspend for all USB devices</help>
+ <valueless/>
+ </properties>
+ </leafNode>
</children>
</node>
</children>
diff --git a/interface-definitions/vpn_openconnect.xml.in b/interface-definitions/vpn_openconnect.xml.in
index 7849d6886..a2f040b2f 100644
--- a/interface-definitions/vpn_openconnect.xml.in
+++ b/interface-definitions/vpn_openconnect.xml.in
@@ -275,7 +275,7 @@
<help>SSL Certificate, SSL Key and CA</help>
</properties>
<children>
- #include <include/pki/ca-certificate.xml.i>
+ #include <include/pki/ca-certificate-multi.xml.i>
#include <include/pki/certificate-key.xml.i>
</children>
</node>
diff --git a/op-mode-definitions/dhcp.xml.in b/op-mode-definitions/dhcp.xml.in
index 0b4a05ffe..eee6937d6 100644
--- a/op-mode-definitions/dhcp.xml.in
+++ b/op-mode-definitions/dhcp.xml.in
@@ -315,4 +315,43 @@
</node>
</children>
</node>
+ <node name="release">
+ <properties>
+ <help>Release specified variable</help>
+ </properties>
+ <children>
+ <node name="dhcp">
+ <properties>
+ <help>Release DHCP client lease</help>
+ </properties>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Release DHCP client lease for specified interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces</script>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/dhcp.py release_client_lease --family inet --interface "$4"</command>
+ </tagNode>
+ </children>
+ </node>
+ <node name="dhcpv6">
+ <properties>
+ <help>Release DHCPv6 client lease</help>
+ </properties>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Release DHCPv6 client lease for specified interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces</script>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/dhcp.py release_client_lease --family inet6 --interface "$4"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
</interfaceDefinition>
diff --git a/op-mode-definitions/generate-ipsec-profile.xml.in b/op-mode-definitions/generate-ipsec-profile.xml.in
index b7203d7d1..afa299da2 100644
--- a/op-mode-definitions/generate-ipsec-profile.xml.in
+++ b/op-mode-definitions/generate-ipsec-profile.xml.in
@@ -28,7 +28,7 @@
<script>${vyos_completion_dir}/list_local_ips.sh --both</script>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7"</command>
+ <command>sudo ${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7"</command>
<children>
<tagNode name="name">
<properties>
@@ -37,7 +37,7 @@
<list>&lt;name&gt;</list>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7" --name "$9"</command>
+ <command>sudo ${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7" --name "$9"</command>
<children>
<tagNode name="profile">
<properties>
@@ -46,7 +46,7 @@
<list>&lt;name&gt;</list>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7" --name "$9" --profile "${11}"</command>
+ <command>sudo ${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7" --name "$9" --profile "${11}"</command>
</tagNode>
</children>
</tagNode>
@@ -57,7 +57,7 @@
<list>&lt;name&gt;</list>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7" --profile "$9"</command>
+ <command>sudo ${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7" --profile "$9"</command>
<children>
<tagNode name="name">
<properties>
@@ -66,7 +66,7 @@
<list>&lt;name&gt;</list>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7" --profile "$9" --name "${11}"</command>
+ <command>sudo ${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7" --profile "$9" --name "${11}"</command>
</tagNode>
</children>
</tagNode>
diff --git a/op-mode-definitions/lldp.xml.in b/op-mode-definitions/lldp.xml.in
index 985262a89..dc1331cc8 100644
--- a/op-mode-definitions/lldp.xml.in
+++ b/op-mode-definitions/lldp.xml.in
@@ -13,6 +13,12 @@
</properties>
<command>${vyos_op_scripts_dir}/lldp.py show_neighbors</command>
<children>
+ <node name="detail">
+ <properties>
+ <help>Show extended detail for LLDP neighbors</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/lldp.py show_neighbors --detail</command>
+ </node>
<tagNode name="interface">
<properties>
<help>Show LLDP for specified interface</help>
@@ -21,6 +27,17 @@
</completionHelp>
</properties>
<command>${vyos_op_scripts_dir}/lldp.py show_neighbors --interface $5</command>
+ <children>
+ <node name="detail">
+ <properties>
+ <help>Show detailed LLDP for specified interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces</script>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/lldp.py show_neighbors --interface $5 --detail</command>
+ </node>
+ </children>
</tagNode>
</children>
</node>
diff --git a/op-mode-definitions/monitor-log.xml.in b/op-mode-definitions/monitor-log.xml.in
index 559952e25..a2d5d924a 100644
--- a/op-mode-definitions/monitor-log.xml.in
+++ b/op-mode-definitions/monitor-log.xml.in
@@ -359,6 +359,47 @@
</properties>
<command>journalctl --no-hostname --boot --follow --unit keepalived.service</command>
</leafNode>
+ <node name="wireless">
+ <properties>
+ <help>Monitor last lines of Wireless interface log</help>
+ </properties>
+ <children>
+ <node name="wpa-supplicant">
+ <properties>
+ <help>Monitor last lines of WPA supplicant</help>
+ </properties>
+ <command>if cli-shell-api existsActive interfaces wireless; then journalctl --no-hostname --boot --follow --unit "wpa_supplicant@*.service"; else echo "No wireless interface configured!"; fi</command>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Monitor last lines of specific wireless interface supplicant</help>
+ <completionHelp>
+ <path>interfaces wireless</path>
+ </completionHelp>
+ </properties>
+ <command>if [[ $(cli-shell-api returnActiveValue interfaces wireless $6 type) == "station" ]]; then journalctl --no-hostname --boot --follow --unit "wpa_supplicant@$6.service"; else echo "Wireless interface $6 not configured as station!"; fi</command>
+ </tagNode>
+ </children>
+ </node>
+ <node name="hostapd">
+ <properties>
+ <help>Monitor last lines of host access point daemon</help>
+ </properties>
+ <command>if cli-shell-api existsActive interfaces wireless; then journalctl --no-hostname --boot --follow --unit "hostapd@*.service"; else echo "No wireless interface configured!"; fi</command>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Monitor last lines of specific host access point interface</help>
+ <completionHelp>
+ <path>interfaces wireless</path>
+ </completionHelp>
+ </properties>
+ <command>if [[ $(cli-shell-api returnActiveValue interfaces wireless $6 type) == "access-point" ]]; then journalctl --no-hostname --boot --follow --unit "hostapd@$6.service"; else echo "Wireless interface $6 not configured as access-point!"; fi</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
</children>
</node>
</children>
diff --git a/op-mode-definitions/pki.xml.in b/op-mode-definitions/pki.xml.in
index f76b4f4e1..254ef08cc 100644
--- a/op-mode-definitions/pki.xml.in
+++ b/op-mode-definitions/pki.xml.in
@@ -490,6 +490,7 @@
<properties>
<help>Show PKI x509 certificates</help>
</properties>
+ <command>sudo ${vyos_op_scripts_dir}/pki.py --action show</command>
<children>
<leafNode name="ca">
<properties>
@@ -570,7 +571,6 @@
</children>
</tagNode>
</children>
- <command>${vyos_op_scripts_dir}/pki.py --action show</command>
</node>
</children>
</node>
diff --git a/op-mode-definitions/restart-ssh.xml.in b/op-mode-definitions/restart-ssh.xml.in
index 6504cc18a..543cafc24 100644
--- a/op-mode-definitions/restart-ssh.xml.in
+++ b/op-mode-definitions/restart-ssh.xml.in
@@ -6,7 +6,7 @@
<properties>
<help>Restart SSH service</help>
</properties>
- <command>if cli-shell-api existsActive service ssh; then sudo systemctl restart ssh.service; else echo "Service SSH not configured"; fi</command>
+ <command>if cli-shell-api existsActive service ssh; then sudo systemctl restart "ssh@*.service"; else echo "Service SSH not configured"; fi</command>
</node>
</children>
</node>
diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in
index c3aa324ba..7ae3b890b 100644
--- a/op-mode-definitions/show-log.xml.in
+++ b/op-mode-definitions/show-log.xml.in
@@ -762,6 +762,47 @@
</properties>
<command>journalctl --no-hostname --boot --unit keepalived.service</command>
</leafNode>
+ <node name="wireless">
+ <properties>
+ <help>Show log for Wireless interface</help>
+ </properties>
+ <children>
+ <node name="wpa-supplicant">
+ <properties>
+ <help>Show log for WPA supplicant</help>
+ </properties>
+ <command>if cli-shell-api existsActive interfaces wireless; then journalctl --no-hostname --boot --unit "wpa_supplicant@*.service"; else echo "No wireless interface configured!"; fi</command>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Show log for specific wireless interface supplicant</help>
+ <completionHelp>
+ <path>interfaces wireless</path>
+ </completionHelp>
+ </properties>
+ <command>if [[ $(cli-shell-api returnActiveValue interfaces wireless $6 type) == "station" ]]; then journalctl --no-hostname --boot --unit "wpa_supplicant@$6.service"; else echo "Wireless interface $6 not configured as station!"; fi</command>
+ </tagNode>
+ </children>
+ </node>
+ <node name="hostapd">
+ <properties>
+ <help>Show log for host access point daemon</help>
+ </properties>
+ <command>if cli-shell-api existsActive interfaces wireless; then journalctl --no-hostname --boot --unit "hostapd@*.service"; else echo "No wireless interface configured!"; fi</command>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Show log for specific host access point daemon interface</help>
+ <completionHelp>
+ <path>interfaces wireless</path>
+ </completionHelp>
+ </properties>
+ <command>if [[ $(cli-shell-api returnActiveValue interfaces wireless $6 type) == "access-point" ]]; then journalctl --no-hostname --boot --unit "hostapd@$6.service"; else echo "Wireless interface $6 not configured as access-point!"; fi</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
<leafNode name="webproxy">
<properties>
<help>Show log for Webproxy</help>
diff --git a/op-mode-definitions/suricata.xml.in b/op-mode-definitions/suricata.xml.in
deleted file mode 100644
index a5025afba..000000000
--- a/op-mode-definitions/suricata.xml.in
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0"?>
-<interfaceDefinition>
- <node name="update">
- <children>
- <node name="suricata">
- <properties>
- <help>Update Suricata</help>
- </properties>
- <command>if test -f /run/suricata/suricata.yaml; then sudo suricata-update --suricata-conf /run/suricata/suricata.yaml; sudo systemctl restart suricata; else echo "Service Suricata not configured"; fi </command>
- </node>
- </children>
- </node>
- <node name="restart">
- <children>
- <node name="suricata">
- <properties>
- <help>Restart Suricata service</help>
- </properties>
- <command>if systemctl is-active --quiet suricata; then sudo systemctl restart suricata.service; else echo "Service Suricata not configured"; fi</command>
- </node>
- </children>
- </node>
-</interfaceDefinition>
diff --git a/op-mode-definitions/traffic-dump.xml.in b/op-mode-definitions/traffic-dump.xml.in
index 76e3ddce5..e86e69736 100644
--- a/op-mode-definitions/traffic-dump.xml.in
+++ b/op-mode-definitions/traffic-dump.xml.in
@@ -8,7 +8,7 @@
</properties>
<children>
<tagNode name="interface">
- <command>sudo tcpdump -i $4</command>
+ <command>${vyos_op_scripts_dir}/tcpdump.py $4</command>
<properties>
<help>Monitor traffic dump from an interface</help>
<completionHelp>
@@ -16,54 +16,15 @@
</completionHelp>
</properties>
<children>
- <node name="verbose">
- <command>sudo tcpdump -vvv -ne -i $4</command>
+ <leafNode name="node.tag">
<properties>
- <help>Provide more detailed packets for each monitored traffic</help>
+ <help>Traffic capture options</help>
+ <completionHelp>
+ <script>${vyos_op_scripts_dir}/tcpdump.py --get-options-nested "${COMP_WORDS[@]}"</script>
+ </completionHelp>
</properties>
- <children>
- <tagNode name="filter">
- <command>sudo tcpdump -vvv -ne -i $4 "${@:6}"</command>
- <properties>
- <help>Monitor traffic matching filter conditions</help>
- </properties>
- </tagNode>
- <tagNode name="save">
- <command>sudo tcpdump -vvv -ne -i $4 -w $6</command>
- <properties>
- <help>Save traffic dump from an interface to a file</help>
- </properties>
- <children>
- <tagNode name="filter">
- <command>sudo tcpdump -vvv -ne -i $4 -w $6 "${@:8}"</command>
- <properties>
- <help>Save a dump of traffic matching filter conditions to a file</help>
- </properties>
- </tagNode>
- </children>
- </tagNode>
- </children>
- </node>
- <tagNode name="filter">
- <command>sudo tcpdump -n -i $4 "${@:6}"</command>
- <properties>
- <help>Monitor traffic matching filter conditions</help>
- </properties>
- </tagNode>
- <tagNode name="save">
- <command>sudo tcpdump -n -i $4 -w $6</command>
- <properties>
- <help>Save traffic dump from an interface to a file</help>
- </properties>
- <children>
- <tagNode name="filter">
- <command>sudo tcpdump -n -i $4 -w $6 "${@:8}"</command>
- <properties>
- <help>Save a dump of traffic matching filter conditions to a file</help>
- </properties>
- </tagNode>
- </children>
- </tagNode>
+ <command>${vyos_op_scripts_dir}/tcpdump.py "${@:4}"</command>
+ </leafNode>
</children>
</tagNode>
</children>
diff --git a/python/vyos/config_mgmt.py b/python/vyos/config_mgmt.py
index 70b6ea203..d518737ca 100644
--- a/python/vyos/config_mgmt.py
+++ b/python/vyos/config_mgmt.py
@@ -81,9 +81,11 @@ def save_config(target, json_out=None):
if rc != 0:
logger.critical(f'save config failed: {out}')
-def unsaved_commits() -> bool:
+def unsaved_commits(allow_missing_config=False) -> bool:
if get_full_version_data()['boot_via'] == 'livecd':
return False
+ if allow_missing_config and not os.path.exists(config_file):
+ return True
tmp_save = '/tmp/config.running'
save_config(tmp_save)
ret = not cmp(tmp_save, config_file, shallow=False)
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 870d7cfda..5a353b110 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -631,7 +631,7 @@ def get_accel_dict(config, base, chap_secrets, with_pki=False):
Return a dictionary with the necessary interface config keys.
"""
- from vyos.cpu import get_core_count
+ from vyos.utils.cpu import get_core_count
from vyos.template import is_ipv4
dict = config.get_config_dict(base, key_mangling=('-', '_'),
diff --git a/python/vyos/configquery.py b/python/vyos/configquery.py
index 71ad5b4f0..5d6ca9be9 100644
--- a/python/vyos/configquery.py
+++ b/python/vyos/configquery.py
@@ -1,4 +1,4 @@
-# Copyright 2021-2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -19,6 +19,8 @@ settings from op mode, and execution of arbitrary op mode commands.
'''
import os
+import json
+import subprocess
from vyos.utils.process import STDOUT
from vyos.utils.process import popen
@@ -27,6 +29,14 @@ from vyos.utils.boot import boot_configuration_complete
from vyos.config import Config
from vyos.configsource import ConfigSourceSession, ConfigSourceString
from vyos.defaults import directories
+from vyos.configtree import ConfigTree
+from vyos.utils.dict import embed_dict
+from vyos.utils.dict import get_sub_dict
+from vyos.utils.dict import mangle_dict_keys
+from vyos.utils.error import cli_shell_api_err
+from vyos.xml_ref import multi_to_list
+from vyos.xml_ref import is_tag
+from vyos.base import Warning
config_file = os.path.join(directories['config'], 'config.boot')
@@ -133,4 +143,50 @@ def query_context(config_query_class=CliShellApiConfigQuery,
run = op_run_class()
return query, run
-
+def verify_mangling(key_mangling):
+ if not (isinstance(key_mangling, tuple) and
+ len(key_mangling) == 2 and
+ isinstance(key_mangling[0], str) and
+ isinstance(key_mangling[1], str)):
+ raise ValueError("key_mangling must be a tuple of two strings")
+
+def op_mode_run(cmd):
+ """ low-level to avoid overhead """
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+ out = p.stdout.read()
+ p.wait()
+ return p.returncode, out.decode()
+
+def op_mode_config_dict(path=None, key_mangling=None,
+ no_tag_node_value_mangle=False,
+ no_multi_convert=False, get_first_key=False):
+
+ if path is None:
+ path = []
+ command = ['/bin/cli-shell-api', '--show-active-only', 'showConfig']
+
+ rc, out = op_mode_run(command + path)
+ if rc == cli_shell_api_err.VYOS_EMPTY_CONFIG:
+ out = ''
+ if rc == cli_shell_api_err.VYOS_INVALID_PATH:
+ Warning(out)
+ return {}
+
+ ct = ConfigTree(out)
+ d = json.loads(ct.to_json())
+ # cli-shell-api output includes last path component if tag node
+ if is_tag(path):
+ config_dict = embed_dict(path[:-1], d)
+ else:
+ config_dict = embed_dict(path, d)
+
+ if not no_multi_convert:
+ config_dict = multi_to_list([], config_dict)
+
+ if key_mangling is not None:
+ verify_mangling(key_mangling)
+ config_dict = mangle_dict_keys(config_dict,
+ key_mangling[0], key_mangling[1],
+ no_tag_node_value_mangle=no_tag_node_value_mangle)
+
+ return get_sub_dict(config_dict, path, get_first_key=get_first_key)
diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py
index beec6010b..ccf2ce8f2 100644
--- a/python/vyos/configsession.py
+++ b/python/vyos/configsession.py
@@ -1,5 +1,4 @@
-# configsession -- the write API for the VyOS running config
-# Copyright (C) 2019-2023 VyOS maintainers and contributors
+# Copyright (C) 2019-2024 VyOS maintainers and contributors
#
# This library is free software; you can redistribute it and/or modify it under the terms of
# the GNU Lesser General Public License as published by the Free Software Foundation;
@@ -12,11 +11,14 @@
# You should have received a copy of the GNU Lesser General Public License along with this library;
# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+# configsession -- the write API for the VyOS running config
+
import os
import re
import sys
import subprocess
+from vyos.defaults import directories
from vyos.utils.process import is_systemd_service_running
from vyos.utils.dict import dict_to_paths
@@ -58,7 +60,7 @@ def inject_vyos_env(env):
env['VYOS_HEADLESS_CLIENT'] = 'vyos_http_api'
env['vyatta_bindir']= '/opt/vyatta/bin'
env['vyatta_cfg_templates'] = '/opt/vyatta/share/vyatta-cfg/templates'
- env['vyatta_configdir'] = '/opt/vyatta/config'
+ env['vyatta_configdir'] = directories['vyos_configdir']
env['vyatta_datadir'] = '/opt/vyatta/share'
env['vyatta_datarootdir'] = '/opt/vyatta/share'
env['vyatta_libdir'] = '/opt/vyatta/lib'
@@ -70,7 +72,7 @@ def inject_vyos_env(env):
env['vyos_bin_dir'] = '/usr/bin'
env['vyos_cfg_templates'] = '/opt/vyatta/share/vyatta-cfg/templates'
env['vyos_completion_dir'] = '/usr/libexec/vyos/completion'
- env['vyos_configdir'] = '/opt/vyatta/config'
+ env['vyos_configdir'] = directories['vyos_configdir']
env['vyos_conf_scripts_dir'] = '/usr/libexec/vyos/conf_mode'
env['vyos_datadir'] = '/opt/vyatta/share'
env['vyos_datarootdir']= '/opt/vyatta/share'
diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py
index e7cd69a8b..9ccd925ce 100644
--- a/python/vyos/defaults.py
+++ b/python/vyos/defaults.py
@@ -1,4 +1,4 @@
-# Copyright 2018-2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2018-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -35,6 +35,7 @@ directories = {
'vyos_udev_dir' : '/run/udev/vyos',
'isc_dhclient_dir' : '/run/dhclient',
'dhcp6_client_dir' : '/run/dhcp6c',
+ 'vyos_configdir' : '/opt/vyatta/config'
}
config_status = '/tmp/vyos-config-status'
@@ -44,7 +45,7 @@ cfg_group = 'vyattacfg'
cfg_vintage = 'vyos'
-commit_lock = '/opt/vyatta/config/.lock'
+commit_lock = os.path.join(directories['vyos_configdir'], '.lock')
component_version_json = os.path.join(directories['data'], 'component-versions.json')
diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py
index d45c9c272..80bb56fa2 100644
--- a/python/vyos/ethtool.py
+++ b/python/vyos/ethtool.py
@@ -16,6 +16,7 @@
import re
from json import loads
+from vyos.utils.network import interface_exists
from vyos.utils.process import popen
# These drivers do not support using ethtool to change the speed, duplex, or
@@ -64,6 +65,9 @@ class Ethtool:
def __init__(self, ifname):
# Get driver used for interface
+ if not interface_exists(ifname):
+ raise ValueError(f'Interface "{ifname}" does not exist!')
+
out, _ = popen(f'ethtool --driver {ifname}')
driver = re.search(r'driver:\s(\w+)', out)
if driver:
diff --git a/python/vyos/ifconfig/macsec.py b/python/vyos/ifconfig/macsec.py
index bde1d9aec..383905814 100644
--- a/python/vyos/ifconfig/macsec.py
+++ b/python/vyos/ifconfig/macsec.py
@@ -66,7 +66,7 @@ class MACsecIf(Interface):
cmd = 'ip macsec add {ifname} rx port 1 address'.format(**self.config)
cmd += f' {peer_config["mac"]}'
self._cmd(cmd)
- # Add the rx-key to the address
+ # Add the encryption key to the address
cmd += f' sa 0 pn 1 on key 01 {peer_config["key"]}'
self._cmd(cmd)
diff --git a/python/vyos/utils/__init__.py b/python/vyos/utils/__init__.py
index 12ef2d3b8..90620071b 100644
--- a/python/vyos/utils/__init__.py
+++ b/python/vyos/utils/__init__.py
@@ -1,4 +1,4 @@
-# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -17,7 +17,9 @@ from vyos.utils import assertion
from vyos.utils import auth
from vyos.utils import boot
from vyos.utils import commit
+from vyos.utils import configfs
from vyos.utils import convert
+from vyos.utils import cpu
from vyos.utils import dict
from vyos.utils import file
from vyos.utils import io
diff --git a/python/vyos/utils/auth.py b/python/vyos/utils/auth.py
index a59858d72..a0b3e1cae 100644
--- a/python/vyos/utils/auth.py
+++ b/python/vyos/utils/auth.py
@@ -1,6 +1,6 @@
# authutils -- miscelanneous functions for handling passwords and publis keys
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2023-2024 VyOS maintainers and contributors
#
# This library is free software; you can redistribute it and/or modify it under the terms of
# the GNU Lesser General Public License as published by the Free Software Foundation;
@@ -11,13 +11,12 @@
# See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along with this library;
-# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import re
from vyos.utils.process import cmd
-
def make_password_hash(password):
""" Makes a password hash for /etc/shadow using mkpasswd """
@@ -39,3 +38,14 @@ def split_ssh_public_key(key_string, defaultname=""):
raise ValueError("Bad key type \'{0}\', must be one of must be one of ssh-rsa, ssh-dss, ecdsa-sha2-nistp<256|384|521> or ssh-ed25519".format(key_type))
return({"type": key_type, "data": key_data, "name": key_name})
+
+def get_current_user() -> str:
+ import os
+ current_user = 'nobody'
+ # During CLI "owner" script execution we use SUDO_USER
+ if 'SUDO_USER' in os.environ:
+ current_user = os.environ['SUDO_USER']
+ # During op-mode or config-mode interactive CLI we use USER
+ elif 'USER' in os.environ:
+ current_user = os.environ['USER']
+ return current_user
diff --git a/python/vyos/utils/configfs.py b/python/vyos/utils/configfs.py
new file mode 100644
index 000000000..8617f0129
--- /dev/null
+++ b/python/vyos/utils/configfs.py
@@ -0,0 +1,37 @@
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+
+def delete_cli_node(cli_path: list):
+ from shutil import rmtree
+ for config_dir in ['VYATTA_TEMP_CONFIG_DIR', 'VYATTA_CHANGES_ONLY_DIR']:
+ tmp = os.path.join(os.environ[config_dir], '/'.join(cli_path))
+ # delete CLI node
+ if os.path.exists(tmp):
+ rmtree(tmp)
+
+def add_cli_node(cli_path: list, value: str=None):
+ from vyos.utils.auth import get_current_user
+ from vyos.utils.file import write_file
+
+ current_user = get_current_user()
+ for config_dir in ['VYATTA_TEMP_CONFIG_DIR', 'VYATTA_CHANGES_ONLY_DIR']:
+ # store new value
+ tmp = os.path.join(os.environ[config_dir], '/'.join(cli_path))
+ write_file(f'{tmp}/node.val', value, user=current_user, group='vyattacfg', mode=0o664)
+ # mark CLI node as modified
+ if config_dir == 'VYATTA_CHANGES_ONLY_DIR':
+ write_file(f'{tmp}/.modified', '', user=current_user, group='vyattacfg', mode=0o664)
diff --git a/python/vyos/cpu.py b/python/vyos/utils/cpu.py
index cae5f5f4d..3bea5ac12 100644
--- a/python/vyos/cpu.py
+++ b/python/vyos/utils/cpu.py
@@ -28,7 +28,6 @@ but nothing is certain.
import re
-
def _read_cpuinfo():
with open('/proc/cpuinfo', 'r') as f:
lines = f.read().strip()
diff --git a/python/vyos/utils/dict.py b/python/vyos/utils/dict.py
index d36b6fcfb..062ab9c81 100644
--- a/python/vyos/utils/dict.py
+++ b/python/vyos/utils/dict.py
@@ -307,6 +307,13 @@ def dict_to_paths(d: dict) -> list:
for r in func(d, []):
yield r
+def embed_dict(p: list[str], d: dict) -> dict:
+ path = p.copy()
+ ret = d
+ while path:
+ ret = {path.pop(): ret}
+ return ret
+
def check_mutually_exclusive_options(d, keys, required=False):
""" Checks if a dict has at most one or only one of
mutually exclusive keys.
diff --git a/python/vyos/utils/error.py b/python/vyos/utils/error.py
new file mode 100644
index 000000000..8d4709bff
--- /dev/null
+++ b/python/vyos/utils/error.py
@@ -0,0 +1,24 @@
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+from enum import IntEnum
+
+class cli_shell_api_err(IntEnum):
+ """ vyatta-cfg/src/vyos-errors.h """
+ VYOS_SUCCESS = 0
+ VYOS_GENERAL_FAILURE = 1
+ VYOS_INVALID_PATH = 2
+ VYOS_EMPTY_CONFIG = 3
+ VYOS_CONFIG_PARSE_ERROR = 4
diff --git a/smoketest/config-tests/container-simple b/smoketest/config-tests/container-simple
index cc80ef4cf..5af365cf9 100644
--- a/smoketest/config-tests/container-simple
+++ b/smoketest/config-tests/container-simple
@@ -11,3 +11,4 @@ set container name c02 allow-host-networks
set container name c02 allow-host-pid
set container name c02 capability 'sys-time'
set container name c02 image 'busybox:stable'
+set container name c02 sysctl parameter kernel.msgmax value '8192' \ No newline at end of file
diff --git a/smoketest/config-tests/dialup-router-wireguard-ipv6 b/smoketest/config-tests/dialup-router-wireguard-ipv6
index c054b4650..814a62d55 100644
--- a/smoketest/config-tests/dialup-router-wireguard-ipv6
+++ b/smoketest/config-tests/dialup-router-wireguard-ipv6
@@ -192,10 +192,6 @@ set service snmp location 'CLOUD'
set system conntrack expect-table-size '2048'
set system conntrack hash-size '32768'
set system conntrack table-size '262144'
-set system conntrack timeout icmp '30'
-set system conntrack timeout other '600'
-set system conntrack timeout udp other '300'
-set system conntrack timeout udp stream '300'
set system domain-name 'vyos.net'
set system host-name 'r1'
set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
@@ -216,6 +212,10 @@ set firewall global-options receive-redirects 'disable'
set firewall global-options send-redirects 'enable'
set firewall global-options source-validation 'disable'
set firewall global-options syn-cookies 'enable'
+set firewall global-options timeout icmp '30'
+set firewall global-options timeout other '600'
+set firewall global-options timeout udp other '300'
+set firewall global-options timeout udp stream '300'
set firewall global-options twa-hazards-protection 'disable'
set firewall group address-group DMZ-RDP-SERVER address '172.16.33.40'
set firewall group address-group DMZ-RDP-SERVER description 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata'
diff --git a/smoketest/configs/container-simple b/smoketest/configs/container-simple
index 82983afb7..b98a440b5 100644
--- a/smoketest/configs/container-simple
+++ b/smoketest/configs/container-simple
@@ -10,6 +10,11 @@ container {
allow-host-pid
cap-add sys-time
image busybox:stable
+ sysctl {
+ parameter kernel.msgmax {
+ value "8192"
+ }
+ }
}
}
interfaces {
diff --git a/smoketest/scripts/cli/base_accel_ppp_test.py b/smoketest/scripts/cli/base_accel_ppp_test.py
index 212dc58ab..c6f6cb804 100644
--- a/smoketest/scripts/cli/base_accel_ppp_test.py
+++ b/smoketest/scripts/cli/base_accel_ppp_test.py
@@ -19,7 +19,7 @@ from configparser import ConfigParser
from vyos.configsession import ConfigSessionError
from vyos.template import is_ipv4
-from vyos.cpu import get_core_count
+from vyos.utils.cpu import get_core_count
from vyos.utils.process import process_named_running
from vyos.utils.process import cmd
diff --git a/smoketest/scripts/cli/base_vyostest_shim.py b/smoketest/scripts/cli/base_vyostest_shim.py
index efaa74fe0..4bcc50453 100644
--- a/smoketest/scripts/cli/base_vyostest_shim.py
+++ b/smoketest/scripts/cli/base_vyostest_shim.py
@@ -74,6 +74,11 @@ class VyOSUnitTestSHIM:
print('del ' + ' '.join(config))
self._session.delete(config)
+ def cli_discard(self):
+ if self.debug:
+ print('DISCARD')
+ self._session.discard()
+
def cli_commit(self):
self._session.commit()
# during a commit there is a process opening commit_lock, and run() returns 0
diff --git a/smoketest/scripts/cli/test_container.py b/smoketest/scripts/cli/test_container.py
index 90f821c60..3dd97a175 100755
--- a/smoketest/scripts/cli/test_container.py
+++ b/smoketest/scripts/cli/test_container.py
@@ -80,6 +80,7 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['name', cont_name, 'image', cont_image])
self.cli_set(base_path + ['name', cont_name, 'allow-host-networks'])
+ self.cli_set(base_path + ['name', cont_name, 'sysctl', 'parameter', 'kernel.msgmax', 'value', '4096'])
# commit changes
self.cli_commit()
@@ -91,6 +92,10 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
# Check for running process
self.assertEqual(process_named_running(PROCESS_NAME), pid)
+ # verify
+ tmp = cmd(f'sudo podman exec -it {cont_name} sysctl kernel.msgmax')
+ self.assertEqual(tmp, 'kernel.msgmax = 4096')
+
def test_cpu_limit(self):
cont_name = 'c2'
diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py
index a4e6840ca..d73895b7f 100755
--- a/smoketest/scripts/cli/test_interfaces_macsec.py
+++ b/smoketest/scripts/cli/test_interfaces_macsec.py
@@ -225,11 +225,11 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
self.cli_commit()
self.cli_delete(self._base_path + [interface, 'security', 'mka'])
- # check validate() - tx-key required
+ # check validate() - key required
with self.assertRaises(ConfigSessionError):
self.cli_commit()
- # check validate() - tx-key length must match cipher
+ # check validate() - key length must match cipher
self.cli_set(self._base_path + [interface, 'security', 'static', 'key', tx_key_2])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
@@ -239,7 +239,7 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
with self.assertRaises(ConfigSessionError):
self.cli_commit()
- # check validate() - enabled peer must have both rx-key and MAC defined
+ # check validate() - enabled peer must have both key and MAC defined
self.cli_set(self._base_path + [interface, 'security', 'static', 'peer', 'TESTPEER'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
@@ -252,7 +252,7 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
self.cli_commit()
self.cli_set(self._base_path + [interface, 'security', 'static', 'peer', 'TESTPEER', 'mac', peer_mac])
- # check validate() - peer rx-key length must match cipher
+ # check validate() - peer key length must match cipher
self.cli_set(self._base_path + [interface, 'security', 'cipher', cipher2])
self.cli_set(self._base_path + [interface, 'security', 'static', 'key', tx_key_2])
with self.assertRaises(ConfigSessionError):
diff --git a/smoketest/scripts/cli/test_service_monitoring_telegraf.py b/smoketest/scripts/cli/test_service_monitoring_telegraf.py
index 3374411f5..886b88683 100755
--- a/smoketest/scripts/cli/test_service_monitoring_telegraf.py
+++ b/smoketest/scripts/cli/test_service_monitoring_telegraf.py
@@ -17,6 +17,7 @@
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.configsession import ConfigSessionError
from vyos.utils.process import process_named_running
from vyos.utils.file import read_file
@@ -63,5 +64,33 @@ class TestMonitoringTelegraf(VyOSUnitTestSHIM.TestCase):
for input in inputs:
self.assertIn(input, config)
+ def test_02_loki(self):
+ label = 'r123'
+ loki_url = 'http://localhost'
+ port = '3100'
+ loki_username = 'VyOS'
+ loki_password = 'PassW0Rd_VyOS'
+
+ self.cli_set(base_path + ['loki', 'url', loki_url])
+ self.cli_set(base_path + ['loki', 'port', port])
+ self.cli_set(base_path + ['loki', 'metric-name-label', label])
+
+ self.cli_set(base_path + ['loki', 'authentication', 'username', loki_username])
+ # password not set
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['loki', 'authentication', 'password', loki_password])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(TELEGRAF_CONF)
+ self.assertIn(f'[[outputs.loki]]', config)
+ self.assertIn(f'domain = "{loki_url}:{port}"', config)
+ self.assertIn(f'metric_name_label = "{label}"', config)
+ self.assertIn(f'username = "{loki_username}"', config)
+ self.assertIn(f'password = "{loki_password}"', config)
+
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py
index b09990c92..d8e325eee 100755
--- a/smoketest/scripts/cli/test_service_ssh.py
+++ b/smoketest/scripts/cli/test_service_ssh.py
@@ -304,6 +304,22 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
for line in ssh_lines:
self.assertIn(line, tmp_sshd_conf)
+ def test_ssh_pubkey_accepted_algorithm(self):
+ algs = ['ssh-ed25519', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384',
+ 'ecdsa-sha2-nistp521', 'ssh-dss', 'ssh-rsa', 'rsa-sha2-256',
+ 'rsa-sha2-512'
+ ]
+
+ expected = 'PubkeyAcceptedAlgorithms '
+ for alg in algs:
+ self.cli_set(base_path + ['pubkey-accepted-algorithm', alg])
+ expected = f'{expected}{alg},'
+ expected = expected[:-1]
+
+ self.cli_commit()
+ tmp_sshd_conf = read_file(SSHD_CONF)
+ self.assertIn(expected, tmp_sshd_conf)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_login.py b/smoketest/scripts/cli/test_system_login.py
index 3f249660d..28abba012 100755
--- a/smoketest/scripts/cli/test_system_login.py
+++ b/smoketest/scripts/cli/test_system_login.py
@@ -24,6 +24,7 @@ from subprocess import Popen, PIPE
from pwd import getpwall
from vyos.configsession import ConfigSessionError
+from vyos.utils.auth import get_current_user
from vyos.utils.process import cmd
from vyos.utils.file import read_file
from vyos.template import inc_ip
@@ -334,5 +335,14 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'secret={tacacs_secret}', nss_tacacs_conf)
self.assertIn(f'server={server}', nss_tacacs_conf)
+ def test_delete_current_user(self):
+ current_user = get_current_user()
+
+ # We are not allowed to delete the current user
+ self.cli_delete(base_path + ['user', current_user])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_discard()
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/src/completion/list_container_sysctl_parameters.sh b/src/completion/list_container_sysctl_parameters.sh
new file mode 100755
index 000000000..cf8d006e5
--- /dev/null
+++ b/src/completion/list_container_sysctl_parameters.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+# Copyright (C) 2024 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+declare -a vals
+eval "vals=($(/sbin/sysctl -N -a|grep -E '^(fs.mqueue|net)\.|^(kernel.msgmax|kernel.msgmnb|kernel.msgmni|kernel.sem|kernel.shmall|kernel.shmmax|kernel.shmmni|kernel.shm_rmid_forced)$'))"
+echo ${vals[@]}
+exit 0
diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py
index 3efeb9b40..ded370a7a 100755
--- a/src/conf_mode/container.py
+++ b/src/conf_mode/container.py
@@ -29,7 +29,7 @@ from vyos.configdict import node_changed
from vyos.configdict import is_node_changed
from vyos.configverify import verify_vrf
from vyos.ifconfig import Interface
-from vyos.cpu import get_core_count
+from vyos.utils.cpu import get_core_count
from vyos.utils.file import write_file
from vyos.utils.process import call
from vyos.utils.process import cmd
@@ -43,6 +43,7 @@ from vyos.template import render
from vyos.xml_ref import default_value
from vyos import ConfigError
from vyos import airbag
+
airbag.enable()
config_containers = '/etc/containers/containers.conf'
@@ -50,16 +51,19 @@ config_registry = '/etc/containers/registries.conf'
config_storage = '/etc/containers/storage.conf'
systemd_unit_path = '/run/systemd/system'
+
def _cmd(command):
if os.path.exists('/tmp/vyos.container.debug'):
print(command)
return cmd(command)
+
def network_exists(name):
# Check explicit name for network, returns True if network exists
c = _cmd(f'podman network ls --quiet --filter name=^{name}$')
return bool(c)
+
# Common functions
def get_config(config=None):
if config:
@@ -86,21 +90,22 @@ def get_config(config=None):
# registry is a tagNode with default values - merge the list from
# default_values['registry'] into the tagNode variables
if 'registry' not in container:
- container.update({'registry' : {}})
+ container.update({'registry': {}})
default_values = default_value(base + ['registry'])
for registry in default_values:
- tmp = {registry : {}}
+ tmp = {registry: {}}
container['registry'] = dict_merge(tmp, container['registry'])
# Delete container network, delete containers
tmp = node_changed(conf, base + ['network'])
- if tmp: container.update({'network_remove' : tmp})
+ if tmp: container.update({'network_remove': tmp})
tmp = node_changed(conf, base + ['name'])
- if tmp: container.update({'container_remove' : tmp})
+ if tmp: container.update({'container_remove': tmp})
return container
+
def verify(container):
# bail out early - looks like removal from running config
if not container:
@@ -125,8 +130,8 @@ def verify(container):
# of image upgrade and deletion.
image = container_config['image']
if run(f'podman image exists {image}') != 0:
- Warning(f'Image "{image}" used in container "{name}" does not exist '\
- f'locally. Please use "add container image {image}" to add it '\
+ Warning(f'Image "{image}" used in container "{name}" does not exist ' \
+ f'locally. Please use "add container image {image}" to add it ' \
f'to the system! Container "{name}" will not be started!')
if 'cpu_quota' in container_config:
@@ -167,11 +172,11 @@ def verify(container):
# We can not use the first IP address of a network prefix as this is used by podman
if ip_address(address) == ip_network(network)[1]:
- raise ConfigError(f'IP address "{address}" can not be used for a container, '\
+ raise ConfigError(f'IP address "{address}" can not be used for a container, ' \
'reserved for the container engine!')
if cnt_ipv4 > 1 or cnt_ipv6 > 1:
- raise ConfigError(f'Only one IP address per address family can be used for '\
+ raise ConfigError(f'Only one IP address per address family can be used for ' \
f'container "{name}". {cnt_ipv4} IPv4 and {cnt_ipv6} IPv6 address(es)!')
if 'device' in container_config:
@@ -186,6 +191,13 @@ def verify(container):
if not os.path.exists(source):
raise ConfigError(f'Device "{dev}" source path "{source}" does not exist!')
+ if 'sysctl' in container_config and 'parameter' in container_config['sysctl']:
+ for var, cfg in container_config['sysctl']['parameter'].items():
+ if 'value' not in cfg:
+ raise ConfigError(f'sysctl parameter {var} has no value assigned!')
+ if var.startswith('net.') and 'allow_host_networks' in container_config:
+ raise ConfigError(f'sysctl parameter {var} cannot be set when using host networking!')
+
if 'environment' in container_config:
for var, cfg in container_config['environment'].items():
if 'value' not in cfg:
@@ -219,7 +231,8 @@ def verify(container):
# Can not set both allow-host-networks and network at the same time
if {'allow_host_networks', 'network'} <= set(container_config):
- raise ConfigError(f'"allow-host-networks" and "network" for "{name}" cannot be both configured at the same time!')
+ raise ConfigError(
+ f'"allow-host-networks" and "network" for "{name}" cannot be both configured at the same time!')
# gid cannot be set without uid
if 'gid' in container_config and 'uid' not in container_config:
@@ -235,8 +248,10 @@ def verify(container):
raise ConfigError(f'prefix for network "{network}" must be defined!')
for prefix in network_config['prefix']:
- if is_ipv4(prefix): v4_prefix += 1
- elif is_ipv6(prefix): v6_prefix += 1
+ if is_ipv4(prefix):
+ v4_prefix += 1
+ elif is_ipv6(prefix):
+ v6_prefix += 1
if v4_prefix > 1:
raise ConfigError(f'Only one IPv4 prefix can be defined for network "{network}"!')
@@ -262,6 +277,7 @@ def verify(container):
return None
+
def generate_run_arguments(name, container_config):
image = container_config['image']
cpu_quota = container_config['cpu_quota']
@@ -269,6 +285,12 @@ def generate_run_arguments(name, container_config):
shared_memory = container_config['shared_memory']
restart = container_config['restart']
+ # Add sysctl options
+ sysctl_opt = ''
+ if 'sysctl' in container_config and 'parameter' in container_config['sysctl']:
+ for k, v in container_config['sysctl']['parameter'].items():
+ sysctl_opt += f" --sysctl {k}={v['value']}"
+
# Add capability options. Should be in uppercase
capabilities = ''
if 'capability' in container_config:
@@ -341,7 +363,7 @@ def generate_run_arguments(name, container_config):
if 'allow_host_pid' in container_config:
host_pid = '--pid host'
- container_base_cmd = f'--detach --interactive --tty --replace {capabilities} --cpus {cpu_quota} ' \
+ container_base_cmd = f'--detach --interactive --tty --replace {capabilities} --cpus {cpu_quota} {sysctl_opt} ' \
f'--memory {memory}m --shm-size {shared_memory}m --memory-swap 0 --restart {restart} ' \
f'--name {name} {hostname} {device} {port} {volume} {env_opt} {label} {uid} {host_pid}'
@@ -375,6 +397,7 @@ def generate_run_arguments(name, container_config):
return f'{container_base_cmd} --no-healthcheck --net {networks} {ip_param} {entrypoint} {image} {command} {command_arguments}'.strip()
+
def generate(container):
# bail out early - looks like removal from running config
if not container:
@@ -387,7 +410,7 @@ def generate(container):
for network, network_config in container['network'].items():
tmp = {
'name': network,
- 'id' : sha256(f'{network}'.encode()).hexdigest(),
+ 'id': sha256(f'{network}'.encode()).hexdigest(),
'driver': 'bridge',
'network_interface': f'pod-{network}',
'subnets': [],
@@ -399,7 +422,7 @@ def generate(container):
}
}
for prefix in network_config['prefix']:
- net = {'subnet' : prefix, 'gateway' : inc_ip(prefix, 1)}
+ net = {'subnet': prefix, 'gateway': inc_ip(prefix, 1)}
tmp['subnets'].append(net)
if is_ipv6(prefix):
@@ -418,11 +441,12 @@ def generate(container):
file_path = os.path.join(systemd_unit_path, f'vyos-container-{name}.service')
run_args = generate_run_arguments(name, container_config)
- render(file_path, 'container/systemd-unit.j2', {'name': name, 'run_args': run_args,},
+ render(file_path, 'container/systemd-unit.j2', {'name': name, 'run_args': run_args, },
formater=lambda _: _.replace("&quot;", '"').replace("&apos;", "'"))
return None
+
def apply(container):
# Delete old containers if needed. We can't delete running container
# Option "--force" allows to delete containers with any status
@@ -485,6 +509,7 @@ def apply(container):
return None
+
if __name__ == '__main__':
try:
c = get_config()
diff --git a/src/conf_mode/interfaces_macsec.py b/src/conf_mode/interfaces_macsec.py
index eb0ca9a8b..3ede4377a 100755
--- a/src/conf_mode/interfaces_macsec.py
+++ b/src/conf_mode/interfaces_macsec.py
@@ -103,9 +103,9 @@ def verify(macsec):
# Logic to check static configuration
if dict_search('security.static', macsec) != None:
- # tx-key must be defined
+ # key must be defined
if dict_search('security.static.key', macsec) == None:
- raise ConfigError('Static MACsec tx-key must be defined.')
+ raise ConfigError('Static MACsec key must be defined.')
tx_len = len(dict_search('security.static.key', macsec))
@@ -119,12 +119,12 @@ def verify(macsec):
if 'peer' not in macsec['security']['static']:
raise ConfigError('Must have at least one peer defined for static MACsec')
- # For every enabled peer, make sure a MAC and rx-key is defined
+ # For every enabled peer, make sure a MAC and key is defined
for peer, peer_config in macsec['security']['static']['peer'].items():
if 'disable' not in peer_config and ('mac' not in peer_config or 'key' not in peer_config):
- raise ConfigError('Every enabled MACsec static peer must have a MAC address and rx-key defined.')
+ raise ConfigError('Every enabled MACsec static peer must have a MAC address and key defined!')
- # check rx-key length against cipher suite
+ # check key length against cipher suite
rx_len = len(peer_config['key'])
if dict_search('security.cipher', macsec) == 'gcm-aes-128' and rx_len != GCM_AES_128_LEN:
diff --git a/src/conf_mode/nat_cgnat.py b/src/conf_mode/nat_cgnat.py
index d429f6e21..3484e5873 100755
--- a/src/conf_mode/nat_cgnat.py
+++ b/src/conf_mode/nat_cgnat.py
@@ -16,11 +16,14 @@
import ipaddress
import jmespath
+import logging
import os
from sys import exit
+from logging.handlers import SysLogHandler
from vyos.config import Config
+from vyos.configdict import is_node_changed
from vyos.template import render
from vyos.utils.process import cmd
from vyos.utils.process import run
@@ -32,6 +35,18 @@ airbag.enable()
nftables_cgnat_config = '/run/nftables-cgnat.nft'
+# Logging
+logger = logging.getLogger('cgnat')
+logger.setLevel(logging.DEBUG)
+
+syslog_handler = SysLogHandler(address="/dev/log")
+syslog_handler.setLevel(logging.INFO)
+
+formatter = logging.Formatter('%(name)s: %(message)s')
+syslog_handler.setFormatter(formatter)
+
+logger.addHandler(syslog_handler)
+
class IPOperations:
def __init__(self, ip_prefix: str):
@@ -104,6 +119,38 @@ class IPOperations:
+ [self.ip_network.broadcast_address]
]
+ def get_prefix_by_ip_range(self) -> list[ipaddress.IPv4Network]:
+ """Return the common prefix for the address range
+
+ Example:
+ % ip = IPOperations('100.64.0.1-100.64.0.5')
+ % ip.get_prefix_by_ip_range()
+ [IPv4Network('100.64.0.1/32'), IPv4Network('100.64.0.2/31'), IPv4Network('100.64.0.4/31')]
+ """
+ # We do not need to convert the IP range to network
+ # if it is already in network format
+ if self.ip_network:
+ return [self.ip_network]
+
+ # Raise an error if the IP range is not in the correct format
+ if '-' not in self.ip_prefix:
+ raise ValueError(
+ 'Invalid IP range format. Please provide the IP range in CIDR format or with "-" separator.'
+ )
+ # Split the IP range and convert it to IP address objects
+ range_start, range_end = self.ip_prefix.split('-')
+ range_start = ipaddress.IPv4Address(range_start)
+ range_end = ipaddress.IPv4Address(range_end)
+
+ # Return the summarized IP networks list
+ return list(ipaddress.summarize_address_range(range_start, range_end))
+
+
+def _delete_conntrack_entries(source_prefixes: list[ipaddress.IPv4Network]) -> None:
+ """Delete all conntrack entries for the list of prefixes"""
+ for source_prefix in source_prefixes:
+ run(f'conntrack -D -s {source_prefix}')
+
def generate_port_rules(
external_hosts: list,
@@ -174,12 +221,31 @@ def get_config(config=None):
with_recursive_defaults=True,
)
+ effective_config = conf.get_config_dict(
+ base,
+ get_first_key=True,
+ key_mangling=('-', '_'),
+ no_tag_node_value_mangle=True,
+ effective=True,
+ )
+
+ # Check if the pool configuration has changed
+ if not conf.exists(base) or is_node_changed(conf, base + ['pool']):
+ config['delete_conntrack_entries'] = {}
+
+ # add running config
+ if effective_config:
+ config['effective'] = effective_config
+
+ if not conf.exists(base):
+ config['deleted'] = {}
+
return config
def verify(config):
# bail out early - looks like removal from running config
- if not config:
+ if 'deleted' in config:
return None
if 'pool' not in config:
@@ -283,7 +349,7 @@ def verify(config):
def generate(config):
- if not config:
+ if 'deleted' in config:
return None
proto_maps = []
@@ -348,13 +414,54 @@ def generate(config):
def apply(config):
- if not config:
+ if 'deleted' in config:
# Cleanup cgnat
cmd('nft delete table ip cgnat')
if os.path.isfile(nftables_cgnat_config):
os.unlink(nftables_cgnat_config)
- return None
- cmd(f'nft --file {nftables_cgnat_config}')
+ else:
+ cmd(f'nft --file {nftables_cgnat_config}')
+
+ # Delete conntrack entries
+ # if the pool configuration has changed
+ if 'delete_conntrack_entries' in config and 'effective' in config:
+ # Prepare the list of internal pool prefixes
+ internal_pool_prefix_list: list[ipaddress.IPv4Network] = []
+
+ # Get effective rules configurations
+ for rule_config in config['effective'].get('rule', {}).values():
+ # Get effective internal pool configuration
+ internal_pool = rule_config['source']['pool']
+ # Find the internal IP ranges for the internal pool
+ internal_ip_ranges: list[str] = config['effective']['pool']['internal'][
+ internal_pool
+ ]['range']
+ # Get the IP prefixes for the internal IP range
+ for internal_range in internal_ip_ranges:
+ ip_prefix: list[ipaddress.IPv4Network] = IPOperations(
+ internal_range
+ ).get_prefix_by_ip_range()
+ # Add the IP prefixes to the list of all internal pool prefixes
+ internal_pool_prefix_list += ip_prefix
+
+ # Delete required sources for conntrack
+ _delete_conntrack_entries(internal_pool_prefix_list)
+
+ # Logging allocations
+ if 'log_allocation' in config:
+ allocations = config['proto_map_elements']
+ allocations = allocations.split(',')
+ for allocation in allocations:
+ try:
+ # Split based on the delimiters used in the nft data format
+ internal_host, rest = allocation.split(' : ')
+ external_host, port_range = rest.split(' . ')
+ # Log the parsed data
+ logger.info(
+ f'Internal host: {internal_host.lstrip()}, external host: {external_host}, Port range: {port_range}')
+ except ValueError as e:
+ # Log error message
+ logger.error(f"Error processing line '{allocation}': {e}")
if __name__ == '__main__':
diff --git a/src/conf_mode/pki.py b/src/conf_mode/pki.py
index f37cac524..4a0e86f32 100755
--- a/src/conf_mode/pki.py
+++ b/src/conf_mode/pki.py
@@ -232,7 +232,7 @@ def get_config(config=None):
path = search['path']
path_str = ' '.join(path + found_path)
- print(f'PKI: Updating config: {path_str} {item_name}')
+ #print(f'PKI: Updating config: {path_str} {item_name}')
if path[0] == 'interfaces':
ifname = found_path[0]
diff --git a/src/conf_mode/service_monitoring_telegraf.py b/src/conf_mode/service_monitoring_telegraf.py
index 40eb13e23..db870aae5 100755
--- a/src/conf_mode/service_monitoring_telegraf.py
+++ b/src/conf_mode/service_monitoring_telegraf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2023 VyOS maintainers and contributors
+# Copyright (C) 2021-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -86,7 +86,8 @@ def get_config(config=None):
monitoring['custom_scripts_dir'] = custom_scripts_dir
monitoring['hostname'] = get_hostname()
monitoring['interfaces_ethernet'] = Section.interfaces('ethernet', vlan=False)
- monitoring['nft_chains'] = get_nft_filter_chains()
+ if conf.exists('firewall'):
+ monitoring['nft_chains'] = get_nft_filter_chains()
# Redefine azure group-metrics 'single-table' and 'table-per-metric'
if 'azure_data_explorer' in monitoring:
@@ -113,6 +114,9 @@ def get_config(config=None):
if not conf.exists(base + ['azure-data-explorer']):
del monitoring['azure_data_explorer']
+ if not conf.exists(base + ['loki']):
+ del monitoring['loki']
+
return monitoring
def verify(monitoring):
@@ -159,6 +163,19 @@ def verify(monitoring):
if 'url' not in monitoring['splunk']:
raise ConfigError(f'Monitoring splunk "url" is mandatory!')
+ # Verify Loki
+ if 'loki' in monitoring:
+ if 'url' not in monitoring['loki']:
+ raise ConfigError(f'Monitoring loki "url" is mandatory!')
+ if 'authentication' in monitoring['loki']:
+ if (
+ 'username' not in monitoring['loki']['authentication']
+ or 'password' not in monitoring['loki']['authentication']
+ ):
+ raise ConfigError(
+ f'Authentication "username" and "password" are mandatory!'
+ )
+
return None
def generate(monitoring):
diff --git a/src/conf_mode/service_snmp.py b/src/conf_mode/service_snmp.py
index 6565ffd60..6f025cc23 100755
--- a/src/conf_mode/service_snmp.py
+++ b/src/conf_mode/service_snmp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2023 VyOS maintainers and contributors
+# Copyright (C) 2018-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -26,10 +26,12 @@ from vyos.snmpv3_hashgen import plaintext_to_md5
from vyos.snmpv3_hashgen import plaintext_to_sha1
from vyos.snmpv3_hashgen import random
from vyos.template import render
-from vyos.utils.process import call
-from vyos.utils.permission import chmod_755
+from vyos.utils.configfs import delete_cli_node
+from vyos.utils.configfs import add_cli_node
from vyos.utils.dict import dict_search
from vyos.utils.network import is_addr_assigned
+from vyos.utils.process import call
+from vyos.utils.permission import chmod_755
from vyos.version import get_version_data
from vyos import ConfigError
from vyos import airbag
@@ -192,12 +194,8 @@ def generate(snmp):
return None
if 'v3' in snmp:
- # net-snmp is now regenerating the configuration file in the background
- # thus we need to re-open and re-read the file as the content changed.
- # After that we can no read the encrypted password from the config and
- # replace the CLI plaintext password with its encrypted version.
- os.environ['vyos_libexec_dir'] = '/usr/libexec/vyos'
-
+ # SNMPv3 uses a hashed password. If CLI defines a plaintext password,
+ # we will hash it in the background and replace the CLI node!
if 'user' in snmp['v3']:
for user, user_config in snmp['v3']['user'].items():
if dict_search('auth.type', user_config) == 'sha':
@@ -212,8 +210,9 @@ def generate(snmp):
snmp['v3']['user'][user]['auth']['encrypted_password'] = tmp
del snmp['v3']['user'][user]['auth']['plaintext_password']
- call(f'/opt/vyatta/sbin/my_set service snmp v3 user "{user}" auth encrypted-password "{tmp}" > /dev/null')
- call(f'/opt/vyatta/sbin/my_delete service snmp v3 user "{user}" auth plaintext-password > /dev/null')
+ cli_base = ['service', 'snmp', 'v3', 'user', user, 'auth']
+ delete_cli_node(cli_base + ['plaintext-password'])
+ add_cli_node(cli_base + ['encrypted-password'], value=tmp)
if dict_search('privacy.plaintext_password', user_config) is not None:
tmp = hash(dict_search('privacy.plaintext_password', user_config),
@@ -222,8 +221,9 @@ def generate(snmp):
snmp['v3']['user'][user]['privacy']['encrypted_password'] = tmp
del snmp['v3']['user'][user]['privacy']['plaintext_password']
- call(f'/opt/vyatta/sbin/my_set service snmp v3 user "{user}" privacy encrypted-password "{tmp}" > /dev/null')
- call(f'/opt/vyatta/sbin/my_delete service snmp v3 user "{user}" privacy plaintext-password > /dev/null')
+ cli_base = ['service', 'snmp', 'v3', 'user', user, 'privacy']
+ delete_cli_node(cli_base + ['plaintext-password'])
+ add_cli_node(cli_base + ['encrypted-password'], value=tmp)
# Write client config file
render(config_file_client, 'snmp/etc.snmp.conf.j2', snmp)
@@ -246,7 +246,7 @@ def apply(snmp):
return None
# start SNMP daemon
- call(f'systemctl restart {systemd_service}')
+ call(f'systemctl reload-or-restart {systemd_service}')
# Enable AgentX in FRR
# This should be done for each daemon individually because common command
diff --git a/src/conf_mode/service_suricata.py b/src/conf_mode/service_suricata.py
deleted file mode 100755
index 69b369e0b..000000000
--- a/src/conf_mode/service_suricata.py
+++ /dev/null
@@ -1,161 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2024 VyOS maintainers and contributors
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import os
-
-from sys import exit
-
-from vyos.base import Warning
-from vyos.config import Config
-from vyos.template import render
-from vyos.utils.process import call
-from vyos import ConfigError
-from vyos import airbag
-airbag.enable()
-
-config_file = '/run/suricata/suricata.yaml'
-rotate_file = '/etc/logrotate.d/suricata'
-
-def get_config(config=None):
- if config:
- conf = config
- else:
- conf = Config()
- base = ['service', 'suricata']
-
- if not conf.exists(base):
- return None
-
- suricata = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True, with_recursive_defaults=True)
-
- return suricata
-
-# https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search
-def topological_sort(source):
- sorted_nodes = []
- permanent_marks = set()
- temporary_marks = set()
-
- def visit(n, v):
- if n in permanent_marks:
- return
- if n in temporary_marks:
- raise ConfigError('At least one cycle exists in the referenced groups')
-
- temporary_marks.add(n)
-
- for m in v.get('group', []):
- m = m.lstrip('!')
- if m not in source:
- raise ConfigError(f'Undefined referenced group "{m}"')
- visit(m, source[m])
-
- temporary_marks.remove(n)
- permanent_marks.add(n)
- sorted_nodes.append((n, v))
-
- while len(permanent_marks) < len(source):
- n = next(n for n in source.keys() if n not in permanent_marks)
- visit(n, source[n])
-
- return sorted_nodes
-
-def verify(suricata):
- if not suricata:
- return None
-
- if 'interface' not in suricata:
- raise ConfigError('No interfaces configured!')
-
- if 'address_group' not in suricata:
- raise ConfigError('No address-group configured!')
-
- if 'port_group' not in suricata:
- raise ConfigError('No port-group configured!')
-
- try:
- topological_sort(suricata['address_group'])
- except (ConfigError,StopIteration) as e:
- raise ConfigError(f'Invalid address-group: {e}')
-
- try:
- topological_sort(suricata['port_group'])
- except (ConfigError,StopIteration) as e:
- raise ConfigError(f'Invalid port-group: {e}')
-
-def generate(suricata):
- if not suricata:
- for file in [config_file, rotate_file]:
- if os.path.isfile(file):
- os.unlink(file)
-
- return None
-
- # Config-related formatters
- def to_var(s:str):
- return s.replace('-','_').upper()
-
- def to_val(s:str):
- return s.replace('-',':')
-
- def to_ref(s:str):
- if s[0] == '!':
- return '!$' + to_var(s[1:])
- return '$' + to_var(s)
-
- def to_config(kind:str):
- def format_group(group):
- (name, value) = group
- property = [to_val(property) for property in value.get(kind,[])]
- group = [to_ref(group) for group in value.get('group',[])]
- return (to_var(name), property + group)
- return format_group
-
- # Format the address group
- suricata['address_group'] = map(to_config('address'),
- topological_sort(suricata['address_group']))
-
- # Format the port group
- suricata['port_group'] = map(to_config('port'),
- topological_sort(suricata['port_group']))
-
- render(config_file, 'ids/suricata.j2', {'suricata': suricata})
- render(rotate_file, 'ids/suricata_logrotate.j2', suricata)
- return None
-
-def apply(suricata):
- systemd_service = 'suricata.service'
- if not suricata or 'interface' not in suricata:
- # Stop suricata service if removed
- call(f'systemctl stop {systemd_service}')
- else:
- Warning('To fetch the latest rules, use "update suricata"; '
- 'To periodically fetch the latest rules, '
- 'use the task scheduler!')
- call(f'systemctl restart {systemd_service}')
-
- return None
-
-if __name__ == '__main__':
- try:
- c = get_config()
- verify(c)
- generate(c)
- apply(c)
- except ConfigError as e:
- print(e)
- exit(1)
diff --git a/src/conf_mode/system_login.py b/src/conf_mode/system_login.py
index 20121f170..439fa645b 100755
--- a/src/conf_mode/system_login.py
+++ b/src/conf_mode/system_login.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -26,14 +26,15 @@ from time import sleep
from vyos.config import Config
from vyos.configverify import verify_vrf
-from vyos.defaults import directories
from vyos.template import render
from vyos.template import is_ipv4
+from vyos.utils.auth import get_current_user
+from vyos.utils.configfs import delete_cli_node
+from vyos.utils.configfs import add_cli_node
from vyos.utils.dict import dict_search
from vyos.utils.file import chown
from vyos.utils.process import cmd
from vyos.utils.process import call
-from vyos.utils.process import rc_cmd
from vyos.utils.process import run
from vyos.utils.process import DEVNULL
from vyos import ConfigError
@@ -125,10 +126,9 @@ def verify(login):
# This check is required as the script is also executed from vyos-router
# init script and there is no SUDO_USER environment variable available
# during system boot.
- if 'SUDO_USER' in os.environ:
- cur_user = os.environ['SUDO_USER']
- if cur_user in login['rm_users']:
- raise ConfigError(f'Attempting to delete current user: {cur_user}')
+ tmp = get_current_user()
+ if tmp in login['rm_users']:
+ raise ConfigError(f'Attempting to delete current user: {tmp}')
if 'user' in login:
system_users = getpwall()
@@ -221,35 +221,13 @@ def generate(login):
login['user'][user]['authentication']['encrypted_password'] = encrypted_password
del login['user'][user]['authentication']['plaintext_password']
- # remove old plaintext password and set new encrypted password
- env = os.environ.copy()
- env['vyos_libexec_dir'] = directories['base']
-
# Set default commands for re-adding user with encrypted password
- del_user_plain = f"system login user {user} authentication plaintext-password"
- add_user_encrypt = f"system login user {user} authentication encrypted-password '{encrypted_password}'"
-
- lvl = env['VYATTA_EDIT_LEVEL']
- # We're in config edit level, for example "edit system login"
- # Change default commands for re-adding user with encrypted password
- if lvl != '/':
- # Replace '/system/login' to 'system login'
- lvl = lvl.strip('/').split('/')
- # Convert command str to list
- del_user_plain = del_user_plain.split()
- # New command exclude level, for example "edit system login"
- del_user_plain = del_user_plain[len(lvl):]
- # Convert string to list
- del_user_plain = " ".join(del_user_plain)
-
- add_user_encrypt = add_user_encrypt.split()
- add_user_encrypt = add_user_encrypt[len(lvl):]
- add_user_encrypt = " ".join(add_user_encrypt)
-
- ret, out = rc_cmd(f"/opt/vyatta/sbin/my_delete {del_user_plain}", env=env)
- if ret: raise ConfigError(out)
- ret, out = rc_cmd(f"/opt/vyatta/sbin/my_set {add_user_encrypt}", env=env)
- if ret: raise ConfigError(out)
+ del_user_plain = ['system', 'login', 'user', user, 'authentication', 'plaintext-password']
+ add_user_encrypt = ['system', 'login', 'user', user, 'authentication', 'encrypted-password']
+
+ delete_cli_node(del_user_plain)
+ add_cli_node(add_user_encrypt, value=encrypted_password)
+
else:
try:
if get_shadow_password(user) == dict_search('authentication.encrypted_password', user_config):
@@ -283,8 +261,6 @@ def generate(login):
if os.path.isfile(tacacs_nss_config_file):
os.unlink(tacacs_nss_config_file)
-
-
# NSS must always be present on the system
render(nss_config_file, 'login/nsswitch.conf.j2', login,
permission=0o644, user='root', group='root')
diff --git a/src/conf_mode/system_option.py b/src/conf_mode/system_option.py
index a2e5db575..ad4c0deae 100755
--- a/src/conf_mode/system_option.py
+++ b/src/conf_mode/system_option.py
@@ -24,6 +24,9 @@ from vyos.configverify import verify_source_interface
from vyos.configverify import verify_interface_exists
from vyos.system import grub_util
from vyos.template import render
+from vyos.utils.dict import dict_search
+from vyos.utils.file import write_file
+from vyos.utils.kernel import check_kmod
from vyos.utils.process import cmd
from vyos.utils.process import is_systemd_service_running
from vyos.utils.network import is_addr_assigned
@@ -35,6 +38,8 @@ airbag.enable()
curlrc_config = r'/etc/curlrc'
ssh_config = r'/etc/ssh/ssh_config.d/91-vyos-ssh-client-options.conf'
systemd_action_file = '/lib/systemd/system/ctrl-alt-del.target'
+usb_autosuspend = r'/etc/udev/rules.d/40-usb-autosuspend.rules'
+kernel_dynamic_debug = r'/sys/kernel/debug/dynamic_debug/control'
time_format_to_locale = {
'12-hour': 'en_US.UTF-8',
'24-hour': 'en_GB.UTF-8'
@@ -85,6 +90,7 @@ def verify(options):
def generate(options):
render(curlrc_config, 'system/curlrc.j2', options)
render(ssh_config, 'system/ssh_config.j2', options)
+ render(usb_autosuspend, 'system/40_usb_autosuspend.j2', options)
cmdline_options = []
if 'kernel' in options:
@@ -155,6 +161,18 @@ def apply(options):
time_format = time_format_to_locale.get(options['time_format'])
cmd(f'localectl set-locale LC_TIME={time_format}')
+ cmd('udevadm control --reload-rules')
+
+ # Enable/disable dynamic debugging for kernel modules
+ modules = ['wireguard']
+ modules_enabled = dict_search('kernel.debug', options) or []
+ for module in modules:
+ if module in modules_enabled:
+ check_kmod(module)
+ write_file(kernel_dynamic_debug, f'module {module} +p')
+ else:
+ write_file(kernel_dynamic_debug, f'module {module} -p')
+
if __name__ == '__main__':
try:
c = get_config()
diff --git a/src/conf_mode/vpn_openconnect.py b/src/conf_mode/vpn_openconnect.py
index 8159fedea..42785134f 100755
--- a/src/conf_mode/vpn_openconnect.py
+++ b/src/conf_mode/vpn_openconnect.py
@@ -21,14 +21,17 @@ from vyos.base import Warning
from vyos.config import Config
from vyos.configverify import verify_pki_certificate
from vyos.configverify import verify_pki_ca_certificate
-from vyos.pki import wrap_certificate
+from vyos.pki import find_chain
+from vyos.pki import encode_certificate
+from vyos.pki import load_certificate
from vyos.pki import wrap_private_key
from vyos.template import render
-from vyos.utils.process import call
+from vyos.utils.dict import dict_search
+from vyos.utils.file import write_file
from vyos.utils.network import check_port_availability
-from vyos.utils.process import is_systemd_service_running
from vyos.utils.network import is_listen_port_bind_service
-from vyos.utils.dict import dict_search
+from vyos.utils.process import call
+from vyos.utils.process import is_systemd_service_running
from vyos import ConfigError
from passlib.hash import sha512_crypt
from time import sleep
@@ -142,7 +145,8 @@ def verify(ocserv):
verify_pki_certificate(ocserv, ocserv['ssl']['certificate'])
if 'ca_certificate' in ocserv['ssl']:
- verify_pki_ca_certificate(ocserv, ocserv['ssl']['ca_certificate'])
+ for ca_cert in ocserv['ssl']['ca_certificate']:
+ verify_pki_ca_certificate(ocserv, ca_cert)
# Check network settings
if "network_settings" in ocserv:
@@ -219,25 +223,36 @@ def generate(ocserv):
if "ssl" in ocserv:
cert_file_path = os.path.join(cfg_dir, 'cert.pem')
cert_key_path = os.path.join(cfg_dir, 'cert.key')
- ca_cert_file_path = os.path.join(cfg_dir, 'ca.pem')
+
if 'certificate' in ocserv['ssl']:
cert_name = ocserv['ssl']['certificate']
pki_cert = ocserv['pki']['certificate'][cert_name]
- with open(cert_file_path, 'w') as f:
- f.write(wrap_certificate(pki_cert['certificate']))
+ loaded_pki_cert = load_certificate(pki_cert['certificate'])
+ loaded_ca_certs = {load_certificate(c['certificate'])
+ for c in ocserv['pki']['ca'].values()} if 'ca' in ocserv['pki'] else {}
+
+ cert_full_chain = find_chain(loaded_pki_cert, loaded_ca_certs)
+
+ write_file(cert_file_path,
+ '\n'.join(encode_certificate(c) for c in cert_full_chain))
if 'private' in pki_cert and 'key' in pki_cert['private']:
- with open(cert_key_path, 'w') as f:
- f.write(wrap_private_key(pki_cert['private']['key']))
+ write_file(cert_key_path, wrap_private_key(pki_cert['private']['key']))
if 'ca_certificate' in ocserv['ssl']:
- ca_name = ocserv['ssl']['ca_certificate']
- pki_ca_cert = ocserv['pki']['ca'][ca_name]
+ ca_cert_file_path = os.path.join(cfg_dir, 'ca.pem')
+ ca_chains = []
+
+ for ca_name in ocserv['ssl']['ca_certificate']:
+ pki_ca_cert = ocserv['pki']['ca'][ca_name]
+ loaded_ca_cert = load_certificate(pki_ca_cert['certificate'])
+ ca_full_chain = find_chain(loaded_ca_cert, loaded_ca_certs)
+ ca_chains.append(
+ '\n'.join(encode_certificate(c) for c in ca_full_chain))
- with open(ca_cert_file_path, 'w') as f:
- f.write(wrap_certificate(pki_ca_cert['certificate']))
+ write_file(ca_cert_file_path, '\n'.join(ca_chains))
# Render config
render(ocserv_conf, 'ocserv/ocserv_config.j2', ocserv)
diff --git a/src/etc/systemd/system/suricata.service.d/10-override.conf b/src/etc/systemd/system/suricata.service.d/10-override.conf
deleted file mode 100644
index 781256cf5..000000000
--- a/src/etc/systemd/system/suricata.service.d/10-override.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-[Service]
-ExecStart=
-ExecStart=/usr/bin/suricata -D --af-packet -c /run/suricata/suricata.yaml --pidfile /run/suricata/suricata.pid
-PIDFile=
-PIDFile=/run/suricata/suricata.pid
-ExecReload=
-ExecReload=/usr/bin/suricatasc -c reload-rules /run/suricata/suricata.socket ; /bin/kill -HUP $MAINPID
-ExecStop=
-ExecStop=/usr/bin/suricatasc -c shutdown /run/suricata/suricata.socket
diff --git a/src/helpers/vyos-config-encrypt.py b/src/helpers/vyos-config-encrypt.py
index 0f9c63b1c..84860bd6a 100755
--- a/src/helpers/vyos-config-encrypt.py
+++ b/src/helpers/vyos-config-encrypt.py
@@ -26,9 +26,8 @@ from tempfile import TemporaryDirectory
from vyos.tpm import clear_tpm_key
from vyos.tpm import read_tpm_key
from vyos.tpm import write_tpm_key
-from vyos.util import ask_input
-from vyos.util import ask_yes_no
-from vyos.util import cmd
+from vyos.utils.io import ask_input, ask_yes_no
+from vyos.utils.process import cmd
persistpath_cmd = '/opt/vyatta/sbin/vyos-persistpath'
mount_paths = ['/config', '/opt/vyatta/etc/config']
diff --git a/src/migration-scripts/firewall/15-to-16 b/src/migration-scripts/firewall/15-to-16
index 7c8d38fe6..28df1256e 100755
--- a/src/migration-scripts/firewall/15-to-16
+++ b/src/migration-scripts/firewall/15-to-16
@@ -42,8 +42,9 @@ if not config.exists(conntrack_base):
for protocol in ['icmp', 'tcp', 'udp', 'other']:
if config.exists(conntrack_base + [protocol]):
- if not config.exists(firewall_base):
+ if not config.exists(firewall_base + ['timeout']):
config.set(firewall_base + ['timeout'])
+
config.copy(conntrack_base + [protocol], firewall_base + ['timeout', protocol])
config.delete(conntrack_base + [protocol])
@@ -52,4 +53,4 @@ try:
f.write(config.to_string())
except OSError as e:
print("Failed to save the modified config: {}".format(e))
- exit(1) \ No newline at end of file
+ exit(1)
diff --git a/src/migration-scripts/firewall/7-to-8 b/src/migration-scripts/firewall/7-to-8
index bbaba113a..17af0f355 100755
--- a/src/migration-scripts/firewall/7-to-8
+++ b/src/migration-scripts/firewall/7-to-8
@@ -84,6 +84,12 @@ if config.exists(zone_base + ['zone']):
config.set_tag(['firewall', 'zone'])
for zone in config.list_nodes(zone_base + ['zone']):
+ if 'interface' in config.list_nodes(zone_base + ['zone', zone]):
+ for iface in config.return_values(zone_base + ['zone', zone, 'interface']):
+ if '+' in iface:
+ config.delete_value(zone_base + ['zone', zone, 'interface'], value=iface)
+ iface = iface.replace('+', '*')
+ config.set(zone_base + ['zone', zone, 'interface'], value=iface, replace=False)
config.copy(zone_base + ['zone', zone], ['firewall', 'zone', zone])
config.delete(zone_base)
diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21
index 14ad0fe4d..05a0c7237 100755
--- a/src/migration-scripts/interfaces/20-to-21
+++ b/src/migration-scripts/interfaces/20-to-21
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -22,6 +22,7 @@ from sys import argv
from vyos.ethtool import Ethtool
from vyos.configtree import ConfigTree
+from vyos.utils.network import interface_exists
if len(argv) < 2:
print("Must specify file name!")
@@ -38,6 +39,10 @@ if not config.exists(base):
exit(0)
for ifname in config.list_nodes(base):
+ # Bail out early if interface vanished from system
+ if not interface_exists(ifname):
+ continue
+
eth = Ethtool(ifname)
# If GRO is enabled by the Kernel - we reflect this on the CLI. If GRO is
diff --git a/src/migration-scripts/nat/6-to-7 b/src/migration-scripts/nat/6-to-7
index a2e735394..25640dec2 100755
--- a/src/migration-scripts/nat/6-to-7
+++ b/src/migration-scripts/nat/6-to-7
@@ -59,6 +59,8 @@ for direction in ['source', 'destination']:
tmp = config.return_value(base + [iface, 'interface-name'])
if tmp != 'any':
config.delete(base + [iface, 'interface-name'])
+ if '+' in tmp:
+ tmp = tmp.replace('+', '*')
config.set(base + [iface, 'name'], value=tmp)
else:
config.delete(base + [iface])
diff --git a/src/op_mode/conntrack_sync.py b/src/op_mode/conntrack_sync.py
index 6c86ff492..f3b09b452 100755
--- a/src/op_mode/conntrack_sync.py
+++ b/src/op_mode/conntrack_sync.py
@@ -19,6 +19,8 @@ import sys
import syslog
import xmltodict
+from tabulate import tabulate
+
import vyos.opmode
from vyos.configquery import CliShellApiConfigQuery
@@ -27,7 +29,6 @@ from vyos.utils.commit import commit_in_progress
from vyos.utils.process import call
from vyos.utils.process import cmd
from vyos.utils.process import run
-from vyos.template import render_to_string
conntrackd_bin = '/usr/sbin/conntrackd'
conntrackd_config = '/run/conntrackd/conntrackd.conf'
@@ -59,6 +60,26 @@ def flush_cache(direction):
if tmp > 0:
raise vyos.opmode.Error('Failed to clear {direction} cache')
+def get_formatted_output(data):
+ data_entries = []
+ for parsed in data:
+ for meta in parsed.get('flow', {}).get('meta', []):
+ direction = meta['@direction']
+ if direction == 'original':
+ src = meta['layer3']['src']
+ dst = meta['layer3']['dst']
+ sport = meta['layer4'].get('sport')
+ dport = meta['layer4'].get('dport')
+ protocol = meta['layer4'].get('@protoname')
+ orig_src = f'{src}:{sport}' if sport else src
+ orig_dst = f'{dst}:{dport}' if dport else dst
+
+ data_entries.append([orig_src, orig_dst, protocol])
+
+ headers = ["Source", "Destination", "Protocol"]
+ output = tabulate(data_entries, headers, tablefmt="simple")
+ return output
+
def from_xml(raw, xml):
out = []
for line in xml.splitlines():
@@ -70,7 +91,7 @@ def from_xml(raw, xml):
if raw:
return out
else:
- return render_to_string('conntrackd/conntrackd.op-mode.j2', {'data' : out})
+ return get_formatted_output(out)
def restart():
is_configured()
diff --git a/src/op_mode/cpu.py b/src/op_mode/cpu.py
index d53663c17..1a0f7392f 100755
--- a/src/op_mode/cpu.py
+++ b/src/op_mode/cpu.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2016-2022 VyOS maintainers and contributors
+# Copyright (C) 2016-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -16,8 +16,9 @@
import sys
-import vyos.cpu
import vyos.opmode
+from vyos.utils.cpu import get_cpus
+from vyos.utils.cpu import get_core_count
from jinja2 import Template
@@ -37,15 +38,15 @@ CPU model(s): {{models | join(", ")}}
""")
def _get_raw_data():
- return vyos.cpu.get_cpus()
+ return get_cpus()
def _format_cpus(cpu_data):
env = {'cpus': cpu_data}
return cpu_template.render(env).strip()
def _get_summary_data():
- count = vyos.cpu.get_core_count()
- cpu_data = vyos.cpu.get_cpus()
+ count = get_core_count()
+ cpu_data = get_cpus()
models = [c['model name'] for c in cpu_data]
env = {'count': count, "models": models}
@@ -79,4 +80,3 @@ if __name__ == '__main__':
except (ValueError, vyos.opmode.Error) as e:
print(e)
sys.exit(1)
-
diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py
index f6029c748..6f57f22a5 100755
--- a/src/op_mode/dhcp.py
+++ b/src/op_mode/dhcp.py
@@ -510,6 +510,16 @@ def renew_client_lease(raw: bool, family: ArgFamily, interface: str):
else:
call(f'systemctl restart dhclient@{interface}.service')
+@_verify_client
+def release_client_lease(raw: bool, family: ArgFamily, interface: str):
+ if not raw:
+ v = 'v6' if family == 'inet6' else ''
+ print(f'Release DHCP{v} client on interface {interface}...')
+ if family == 'inet6':
+ call(f'systemctl stop dhcp6c@{interface}.service')
+ else:
+ call(f'systemctl stop dhclient@{interface}.service')
+
if __name__ == '__main__':
try:
res = vyos.opmode.run(sys.modules[__name__])
diff --git a/src/op_mode/ikev2_profile_generator.py b/src/op_mode/ikev2_profile_generator.py
index 169a15840..b193d8109 100755
--- a/src/op_mode/ikev2_profile_generator.py
+++ b/src/op_mode/ikev2_profile_generator.py
@@ -21,6 +21,7 @@ from socket import getfqdn
from cryptography.x509.oid import NameOID
from vyos.configquery import ConfigTreeQuery
+from vyos.config import config_dict_mangle_acme
from vyos.pki import CERT_BEGIN
from vyos.pki import CERT_END
from vyos.pki import find_chain
@@ -123,6 +124,8 @@ pki_base = ['pki']
conf = ConfigTreeQuery()
if not conf.exists(config_base):
exit('IPsec remote-access is not configured!')
+if not conf.exists(pki_base):
+ exit('PKI is not configured!')
profile_name = 'VyOS IKEv2 Profile'
if args.profile:
@@ -147,30 +150,36 @@ tmp = getfqdn().split('.')
tmp = reversed(tmp)
data['rfqdn'] = '.'.join(tmp)
-pki = conf.get_config_dict(pki_base, get_first_key=True)
-cert_name = data['authentication']['x509']['certificate']
-
-cert_data = load_certificate(pki['certificate'][cert_name]['certificate'])
-data['cert_common_name'] = cert_data.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
-data['ca_common_name'] = cert_data.issuer.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
-data['ca_certificates'] = []
-
-loaded_ca_certs = {load_certificate(c['certificate'])
- for c in pki['ca'].values()} if 'ca' in pki else {}
-
-for ca_name in data['authentication']['x509']['ca_certificate']:
- loaded_ca_cert = load_certificate(pki['ca'][ca_name]['certificate'])
- ca_full_chain = find_chain(loaded_ca_cert, loaded_ca_certs)
- for ca in ca_full_chain:
- tmp = {
- 'ca_name' : ca.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value,
- 'ca_chain' : encode_certificate(ca).replace(CERT_BEGIN, '').replace(CERT_END, '').replace('\n', ''),
- }
- data['ca_certificates'].append(tmp)
-
-# Remove duplicate list entries for CA certificates, as they are added by their common name
-# https://stackoverflow.com/a/9427216
-data['ca_certificates'] = [dict(t) for t in {tuple(d.items()) for d in data['ca_certificates']}]
+if args.os == 'ios':
+ pki = conf.get_config_dict(pki_base, get_first_key=True)
+ if 'certificate' in pki:
+ for certificate in pki['certificate']:
+ pki['certificate'][certificate] = config_dict_mangle_acme(certificate, pki['certificate'][certificate])
+
+ cert_name = data['authentication']['x509']['certificate']
+
+
+ cert_data = load_certificate(pki['certificate'][cert_name]['certificate'])
+ data['cert_common_name'] = cert_data.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
+ data['ca_common_name'] = cert_data.issuer.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
+ data['ca_certificates'] = []
+
+ loaded_ca_certs = {load_certificate(c['certificate'])
+ for c in pki['ca'].values()} if 'ca' in pki else {}
+
+ for ca_name in data['authentication']['x509']['ca_certificate']:
+ loaded_ca_cert = load_certificate(pki['ca'][ca_name]['certificate'])
+ ca_full_chain = find_chain(loaded_ca_cert, loaded_ca_certs)
+ for ca in ca_full_chain:
+ tmp = {
+ 'ca_name' : ca.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value,
+ 'ca_chain' : encode_certificate(ca).replace(CERT_BEGIN, '').replace(CERT_END, '').replace('\n', ''),
+ }
+ data['ca_certificates'].append(tmp)
+
+ # Remove duplicate list entries for CA certificates, as they are added by their common name
+ # https://stackoverflow.com/a/9427216
+ data['ca_certificates'] = [dict(t) for t in {tuple(d.items()) for d in data['ca_certificates']}]
esp_proposals = conf.get_config_dict(ipsec_base + ['esp-group', data['esp_group'], 'proposal'],
key_mangling=('-', '_'), get_first_key=True)
diff --git a/src/op_mode/lldp.py b/src/op_mode/lldp.py
index 58cfce443..fac622b81 100755
--- a/src/op_mode/lldp.py
+++ b/src/op_mode/lldp.py
@@ -120,7 +120,12 @@ def _get_formatted_output(raw_data):
tmp.append('')
# Remote interface
- interface = jmespath.search('port.descr', values)
+ interface = None
+ if jmespath.search('port.id.type', values) == 'ifname':
+ # Remote peer has explicitly returned the interface name as the PortID
+ interface = jmespath.search('port.id.value', values)
+ if not interface:
+ interface = jmespath.search('port.descr', values)
if not interface:
interface = jmespath.search('port.id.value', values)
if not interface:
@@ -136,11 +141,17 @@ def _get_formatted_output(raw_data):
@_verify
def show_neighbors(raw: bool, interface: typing.Optional[str], detail: typing.Optional[bool]):
- lldp_data = _get_raw_data(interface=interface, detail=detail)
- if raw:
- return lldp_data
- else:
- return _get_formatted_output(lldp_data)
+ if raw or not detail:
+ lldp_data = _get_raw_data(interface=interface, detail=detail)
+ if raw:
+ return lldp_data
+ else:
+ return _get_formatted_output(lldp_data)
+ else: # non-raw, detail
+ tmp = 'lldpcli -f text show neighbors details'
+ if interface:
+ tmp += f' ports {interface}'
+ return cmd(tmp)
if __name__ == "__main__":
try:
diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py
index 361b60e0e..9ce166c7d 100755
--- a/src/op_mode/pki.py
+++ b/src/op_mode/pki.py
@@ -426,11 +426,15 @@ def generate_ca_certificate_sign(name, ca_name, install=False, file=False):
return None
cert = generate_certificate(cert_req, ca_cert, ca_private_key, is_ca=True, is_sub_ca=True)
- passphrase = ask_passphrase()
+
+ passphrase = None
+ if private_key is not None:
+ passphrase = ask_passphrase()
if not install and not file:
print(encode_certificate(cert))
- print(encode_private_key(private_key, passphrase=passphrase))
+ if private_key is not None:
+ print(encode_private_key(private_key, passphrase=passphrase))
return None
if install:
@@ -438,7 +442,8 @@ def generate_ca_certificate_sign(name, ca_name, install=False, file=False):
if file:
write_file(f'{name}.pem', encode_certificate(cert))
- write_file(f'{name}.key', encode_private_key(private_key, passphrase=passphrase))
+ if private_key is not None:
+ write_file(f'{name}.key', encode_private_key(private_key, passphrase=passphrase))
def generate_certificate_sign(name, ca_name, install=False, file=False):
ca_dict = get_config_ca_certificate(ca_name)
@@ -492,11 +497,15 @@ def generate_certificate_sign(name, ca_name, install=False, file=False):
return None
cert = generate_certificate(cert_req, ca_cert, ca_private_key, is_ca=False)
- passphrase = ask_passphrase()
+
+ passphrase = None
+ if private_key is not None:
+ passphrase = ask_passphrase()
if not install and not file:
print(encode_certificate(cert))
- print(encode_private_key(private_key, passphrase=passphrase))
+ if private_key is not None:
+ print(encode_private_key(private_key, passphrase=passphrase))
return None
if install:
@@ -504,7 +513,8 @@ def generate_certificate_sign(name, ca_name, install=False, file=False):
if file:
write_file(f'{name}.pem', encode_certificate(cert))
- write_file(f'{name}.key', encode_private_key(private_key, passphrase=passphrase))
+ if private_key is not None:
+ write_file(f'{name}.key', encode_private_key(private_key, passphrase=passphrase))
def generate_certificate_selfsign(name, install=False, file=False):
private_key, key_type = generate_private_key()
diff --git a/src/op_mode/powerctrl.py b/src/op_mode/powerctrl.py
index 6c8f802b5..cb4a175dd 100755
--- a/src/op_mode/powerctrl.py
+++ b/src/op_mode/powerctrl.py
@@ -110,7 +110,7 @@ def check_unsaved_config():
from vyos.config_mgmt import unsaved_commits
from vyos.utils.boot import boot_configuration_success
- if unsaved_commits() and boot_configuration_success():
+ if unsaved_commits(allow_missing_config=True) and boot_configuration_success():
print("Warning: there are unsaved configuration changes!")
print("Run 'save' command if you do not want to lose those changes after reboot/shutdown.")
else:
diff --git a/src/op_mode/show_techsupport_report.py b/src/op_mode/show_techsupport_report.py
index 230fb252d..32cf67778 100644
--- a/src/op_mode/show_techsupport_report.py
+++ b/src/op_mode/show_techsupport_report.py
@@ -14,10 +14,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import os
+import sys
from typing import List
-from vyos.utils.process import rc_cmd
from vyos.ifconfig import Section
from vyos.ifconfig import Interface
+from vyos.utils.process import rc_cmd
def print_header(command: str) -> None:
@@ -50,7 +52,15 @@ def execute_command(command: str, header_text: str) -> None:
print_header(header_text)
try:
rc, output = rc_cmd(command)
- print(output)
+ # Enable unbuffered print param to improve responsiveness of printed
+ # output to end user
+ print(output, flush=True)
+ # Exit gracefully when user interrupts program output
+ # Flush standard streams; redirect remaining output to devnull
+ # Resolves T5633: Bug #1 and 3
+ except (BrokenPipeError, KeyboardInterrupt):
+ os.dup2(os.open(os.devnull, os.O_WRONLY), sys.stdout.fileno())
+ sys.exit(1)
except Exception as e:
print(f"Error executing command: {command}")
print(f"Error message: {e}")
@@ -155,13 +165,13 @@ def show_route() -> None:
"show ip route supernets-only",
"show ip route table all",
"show ip route vrf all",
- "show ipv6 route bgp | head 108",
+ "show ipv6 route bgp | head -108",
"show ipv6 route cache",
"show ipv6 route connected",
"show ipv6 route forward",
"show ipv6 route isis",
"show ipv6 route kernel",
- "show ipv6 route ospf",
+ "show ipv6 route ospfv3",
"show ipv6 route rip",
"show ipv6 route static",
"show ipv6 route summary",
@@ -179,8 +189,9 @@ def show_firewall() -> None:
def show_system() -> None:
"""Prints system parameters."""
- execute_command(op('show system image version'), 'Show System Image Version')
- execute_command(op('show system image storage'), 'Show System Image Storage')
+ execute_command(op('show version'), 'Show System Version')
+ execute_command(op('show system storage'), 'Show System Storage')
+ execute_command(op('show system image details'), 'Show System Image Details')
def show_date() -> None:
diff --git a/src/op_mode/tcpdump.py b/src/op_mode/tcpdump.py
new file mode 100644
index 000000000..607b59603
--- /dev/null
+++ b/src/op_mode/tcpdump.py
@@ -0,0 +1,165 @@
+#! /usr/bin/env python3
+
+# Copyright (C) 2024 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+
+from vyos.utils.process import call
+
+options = {
+ 'dump': {
+ 'cmd': '{command} -A',
+ 'type': 'noarg',
+ 'help': 'Print each packet (minus its link level header) in ASCII.'
+ },
+ 'hexdump': {
+ 'cmd': '{command} -X',
+ 'type': 'noarg',
+ 'help': 'Print each packet (minus its link level header) in both hex and ASCII.'
+ },
+ 'filter': {
+ 'cmd': '{command} \'{value}\'',
+ 'type': '<pcap-filter>',
+ 'help': 'Match traffic for capture and display with a pcap-filter expression.'
+ },
+ 'numeric': {
+ 'cmd': '{command} -nn',
+ 'type': 'noarg',
+ 'help': 'Do not attempt to resolve addresses, protocols or services to names.'
+ },
+ 'save': {
+ 'cmd': '{command} -w {value}',
+ 'type': '<file>',
+ 'help': 'Write captured raw packets to <file> rather than parsing or printing them out.'
+ },
+ 'verbose': {
+ 'cmd': '{command} -vvv -ne',
+ 'type': 'noarg',
+ 'help': 'Parse packets with increased detail output, including link-level headers and extended decoding protocol sanity checks.'
+ },
+}
+
+tcpdump = 'sudo /usr/bin/tcpdump'
+
+class List(list):
+ def first(self):
+ return self.pop(0) if self else ''
+
+ def last(self):
+ return self.pop() if self else ''
+
+ def prepend(self, value):
+ self.insert(0, value)
+
+
+def completion_failure(option: str) -> None:
+ """
+ Shows failure message after TAB when option is wrong
+ :param option: failure option
+ :type str:
+ """
+ sys.stderr.write('\n\n Invalid option: {}\n\n'.format(option))
+ sys.stdout.write('<nocomps>')
+ sys.exit(1)
+
+
+def expansion_failure(option, completions):
+ reason = 'Ambiguous' if completions else 'Invalid'
+ sys.stderr.write(
+ '\n\n {} command: {} [{}]\n\n'.format(reason, ' '.join(sys.argv),
+ option))
+ if completions:
+ sys.stderr.write(' Possible completions:\n ')
+ sys.stderr.write('\n '.join(completions))
+ sys.stderr.write('\n')
+ sys.stdout.write('<nocomps>')
+ sys.exit(1)
+
+
+def complete(prefix):
+ return [o for o in options if o.startswith(prefix)]
+
+
+def convert(command, args):
+ while args:
+ shortname = args.first()
+ longnames = complete(shortname)
+ if len(longnames) != 1:
+ expansion_failure(shortname, longnames)
+ longname = longnames[0]
+ if options[longname]['type'] == 'noarg':
+ command = options[longname]['cmd'].format(
+ command=command, value='')
+ elif not args:
+ sys.exit(f'monitor traffic: missing argument for {longname} option')
+ else:
+ command = options[longname]['cmd'].format(
+ command=command, value=args.first())
+ return command
+
+
+if __name__ == '__main__':
+ args = List(sys.argv[1:])
+ ifname = args.first()
+
+ # Slightly simplified & tweaked version of the code from mtr.py - it may be
+ # worthwhile to combine and centralise this in a common module.
+ if ifname == '--get-options-nested':
+ args.first() # pop monitor
+ args.first() # pop traffic
+ args.first() # pop interface
+ args.first() # pop <ifname>
+ usedoptionslist = []
+ while args:
+ option = args.first() # pop option
+ matched = complete(option) # get option parameters
+ usedoptionslist.append(option) # list of used options
+ # Select options
+ if not args:
+ # remove from Possible completions used options
+ for o in usedoptionslist:
+ if o in matched:
+ matched.remove(o)
+ if not matched:
+ sys.stdout.write('<nocomps>')
+ else:
+ sys.stdout.write(' '.join(matched))
+ sys.exit(0)
+
+ if len(matched) > 1:
+ sys.stdout.write(' '.join(matched))
+ sys.exit(0)
+ # If option doesn't have value
+ if matched:
+ if options[matched[0]]['type'] == 'noarg':
+ continue
+ else:
+ # Unexpected option
+ completion_failure(option)
+
+ value = args.first() # pop option's value
+ if not args:
+ matched = complete(option)
+ helplines = options[matched[0]]['type']
+ # Run helpfunction to get list of possible values
+ if 'helpfunction' in options[matched[0]]:
+ result = options[matched[0]]['helpfunction']()
+ if result:
+ helplines = '\n' + ' '.join(result)
+ sys.stdout.write(helplines)
+ sys.exit(0)
+
+ command = convert(tcpdump, args)
+ call(f'{command} -i {ifname}')
diff --git a/src/op_mode/uptime.py b/src/op_mode/uptime.py
index 059a4c3f6..559eed24c 100755
--- a/src/op_mode/uptime.py
+++ b/src/op_mode/uptime.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2023 VyOS maintainers and contributors
+# Copyright (C) 2021-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
@@ -29,8 +29,8 @@ def _get_uptime_seconds():
def _get_load_averages():
from re import search
+ from vyos.utils.cpu import get_core_count
from vyos.utils.process import cmd
- from vyos.cpu import get_core_count
data = cmd("uptime")
matches = search(r"load average:\s*(?P<one>[0-9\.]+)\s*,\s*(?P<five>[0-9\.]+)\s*,\s*(?P<fifteen>[0-9\.]+)\s*", data)
diff --git a/src/services/vyos-configd b/src/services/vyos-configd
index c89c486e5..d92b539c8 100755
--- a/src/services/vyos-configd
+++ b/src/services/vyos-configd
@@ -179,8 +179,13 @@ def initialization(socket):
pid_string = socket.recv().decode("utf-8", "ignore")
resp = "pid"
socket.send(resp.encode())
+ sudo_user_string = socket.recv().decode("utf-8", "ignore")
+ resp = "sudo_user"
+ socket.send(resp.encode())
logger.debug(f"config session pid is {pid_string}")
+ logger.debug(f"config session sudo_user is {sudo_user_string}")
+
try:
session_out = os.readlink(f"/proc/{pid_string}/fd/1")
session_mode = 'w'
@@ -192,6 +197,8 @@ def initialization(socket):
session_out = script_stdout_log
session_mode = 'a'
+ os.environ['SUDO_USER'] = sudo_user_string
+
try:
configsource = ConfigSourceString(running_config_text=active_string,
session_config_text=session_string)
@@ -266,9 +273,6 @@ if __name__ == '__main__':
cfg_group = grp.getgrnam(CFG_GROUP)
os.setgid(cfg_group.gr_gid)
- os.environ['SUDO_USER'] = 'vyos'
- os.environ['SUDO_GID'] = str(cfg_group.gr_gid)
-
def sig_handler(signum, frame):
shutdown()
diff --git a/src/shim/vyshim.c b/src/shim/vyshim.c
index 41723e7a4..4d836127d 100644
--- a/src/shim/vyshim.c
+++ b/src/shim/vyshim.c
@@ -178,6 +178,13 @@ int initialization(void* Requester)
strsep(&pid_val, "_");
debug_print("config session pid: %s\n", pid_val);
+ char *sudo_user = getenv("SUDO_USER");
+ if (!sudo_user) {
+ char nobody[] = "nobody";
+ sudo_user = nobody;
+ }
+ debug_print("sudo_user is %s\n", sudo_user);
+
debug_print("Sending init announcement\n");
char *init_announce = mkjson(MKJSON_OBJ, 1,
MKJSON_STRING, "type", "init");
@@ -240,6 +247,10 @@ int initialization(void* Requester)
zmq_recv(Requester, buffer, 16, 0);
debug_print("Received pid receipt\n");
+ debug_print("Sending config session sudo_user\n");
+ zmq_send(Requester, sudo_user, strlen(sudo_user), 0);
+ zmq_recv(Requester, buffer, 16, 0);
+ debug_print("Received sudo_user receipt\n");
return 0;
}