diff options
38 files changed, 432 insertions, 205 deletions
diff --git a/.github/workflows/cleanup-mirror-pr-branch.yml b/.github/workflows/cleanup-mirror-pr-branch.yml new file mode 100644 index 000000000..c5de9ab73 --- /dev/null +++ b/.github/workflows/cleanup-mirror-pr-branch.yml @@ -0,0 +1,35 @@ +name: Cleanup pr mirror branch + +on: + pull_request: + types: [closed] + branches: + - current + workflow_dispatch: + inputs: + branch: + description: 'Branch to delete' + required: true + +permissions: + contents: write + +jobs: + delete_branch: + if: ${{ (github.event_name == 'workflow_dispatch' || startsWith(github.event.pull_request.head.ref, 'mirror/')) && github.repository_owner != 'vyos' }} + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Delete branch + run: | + branch=${{ github.event_name == 'workflow_dispatch' && github.event.inputs.branch || github.event.pull_request.head.ref }} + if [[ $branch != mirror/* ]]; then + echo "Branch name to clean must start with 'mirror/'" + exit 1 + fi + repo=${{ github.repository }} + git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} + git push origin --delete $branch diff --git a/.github/workflows/package-smoketest.yml b/.github/workflows/package-smoketest.yml index 91c968c82..d352bd3cb 100644 --- a/.github/workflows/package-smoketest.yml +++ b/.github/workflows/package-smoketest.yml @@ -17,6 +17,7 @@ env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed for PR comments BUILD_BY: autobuild@vyos.net DEBIAN_MIRROR: http://deb.debian.org/debian/ + DEBIAN_SECURITY_MIRROR: http://deb.debian.org/debian-security VYOS_MIRROR: https://packages.vyos.net/repositories/current/ jobs: @@ -56,6 +57,7 @@ jobs: --build-type release \ --custom-package vyos-1x-smoketest \ --debian-mirror $DEBIAN_MIRROR \ + --debian-security-mirror $DEBIAN_SECURITY_MIRROR \ --version ${{ steps.version.outputs.build_version }} \ --vyos-mirror $VYOS_MIRROR \ generic diff --git a/.github/workflows/repo-sync.yml b/.github/workflows/repo-sync.yml deleted file mode 100644 index 752cf947a..000000000 --- a/.github/workflows/repo-sync.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Repo-sync - -on: - pull_request_target: - types: - - closed - branches: - - current - - equuleus - workflow_dispatch: - -jobs: - trigger-sync: - uses: vyos/.github/.github/workflows/trigger-repo-sync.yml@current - secrets: - REMOTE_REPO: ${{ secrets.REMOTE_REPO }} - REMOTE_OWNER: ${{ secrets.REMOTE_OWNER }} - PAT: ${{ secrets.PAT }} diff --git a/.github/workflows/trigger-pr-mirror-repo-sync.yml b/.github/workflows/trigger-pr-mirror-repo-sync.yml new file mode 100644 index 000000000..9653c2dca --- /dev/null +++ b/.github/workflows/trigger-pr-mirror-repo-sync.yml @@ -0,0 +1,38 @@ +name: Trigger Mirror PR and Repo Sync +on: + pull_request_target: + types: + - closed + branches: + - current + +env: + GH_TOKEN: ${{ secrets.PAT }} + +concurrency: + group: trigger-pr-mirror-repo-sync-${{ github.event.pull_request.base.ref }} + cancel-in-progress: false +jobs: + trigger-mirror-pr-repo-sync: + if: ${{ github.repository_owner == 'vyos' }} + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: write + + steps: + - name: Bullfrog Secure Runner + uses: bullfrogsec/bullfrog@v0 + with: + egress-policy: audit + + - name: Trigger repo sync + shell: bash + run: | + echo "Triggering sync workflow for ${{ secrets.REMOTE_OWNER }}/${{ secrets.REMOTE_REPO }}" + echo "Triggering sync workflow with PAT ${{ secrets.PAT }}" + curl -X POST \ + -H "Accept: application/vnd.github.everest-preview+json" \ + -H "Authorization: Bearer ${{ secrets.PAT }}" \ + https://api.github.com/repos/${{ secrets.REMOTE_OWNER }}/${{ secrets.REMOTE_REPO }}/actions/workflows/mirror-pr-and-sync.yml/dispatches \ + -d '{"ref":"git-actions", "inputs": {"pr_number": "${{ github.event.pull_request.number }}", "sync_branch": "${{ github.event.pull_request.base.ref }}"}}' diff --git a/.github/workflows/trigger-pr.yml b/.github/workflows/trigger-pr.yml deleted file mode 100644 index f88458a81..000000000 --- a/.github/workflows/trigger-pr.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Trigger PR - -on: - pull_request_target: - types: - - closed - branches: - - circinus - -jobs: - trigger-PR: - uses: vyos/.github/.github/workflows/trigger-pr.yml@current - with: - source_branch: 'circinus' - target_branch: 'circinus' - secrets: - REMOTE_REPO: ${{ secrets.REMOTE_REPO }} - REMOTE_OWNER: ${{ secrets.REMOTE_OWNER }} - PAT: ${{ secrets.PAT }} diff --git a/.gitignore b/.gitignore index c597d9c84..d1bfc91d7 100644 --- a/.gitignore +++ b/.gitignore @@ -147,6 +147,7 @@ python/vyos/xml_ref/cache.py python/vyos/xml_ref/pkg_cache/*_cache.py python/vyos/xml_ref/op_cache.py python/vyos/xml_ref/pkg_cache/*_op_cache.py +data/reftree.cache # autogenerated vyos-configd JSON definition data/configd-include.json diff --git a/CODEOWNERS b/CODEOWNERS index 191394298..4891a0325 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1,2 @@ -* @vyos/reviewers
\ No newline at end of file +# Users from reviewers github team +* @dmbaturin @sarthurdev @jestabro @sever-sever @c-po @fett0 @nicolas-fort @zdc @@ -28,7 +28,7 @@ interface_definitions: $(config_xml_obj) find $(BUILD_DIR)/interface-definitions -type f -name "*.xml" | xargs -I {} $(CURDIR)/scripts/build-command-templates {} $(CURDIR)/schema/interface_definition.rng $(TMPL_DIR) || exit 1 - $(CURDIR)/python/vyos/xml_ref/generate_cache.py --xml-dir $(BUILD_DIR)/interface-definitions || exit 1 + $(CURDIR)/python/vyos/xml_ref/generate_cache.py --xml-dir $(BUILD_DIR)/interface-definitions --internal-cache $(DATA_DIR)/reftree.cache || exit 1 # XXX: delete top level node.def's that now live in other packages # IPSec VPN EAP-RADIUS does not support source-address diff --git a/data/templates/accel-ppp/chap-secrets.ipoe.j2 b/data/templates/accel-ppp/chap-secrets.ipoe.j2 index 43083e22e..dd85160c0 100644 --- a/data/templates/accel-ppp/chap-secrets.ipoe.j2 +++ b/data/templates/accel-ppp/chap-secrets.ipoe.j2 @@ -6,7 +6,7 @@ {% if mac_config.vlan is vyos_defined %} {% set iface = iface ~ '.' ~ mac_config.vlan %} {% endif %} -{{ "%-11s" | format(iface) }} * {{ mac | lower }} * {{ mac_config.rate_limit.download ~ '/' ~ mac_config.rate_limit.upload if mac_config.rate_limit.download is vyos_defined and mac_config.rate_limit.upload is vyos_defined }} +{{ "%-11s" | format(iface) }} * {{ mac | lower }} {{ mac_config.static_ip if mac_config.static_ip is vyos_defined else '*' }} {{ mac_config.rate_limit.download ~ '/' ~ mac_config.rate_limit.upload if mac_config.rate_limit.download is vyos_defined and mac_config.rate_limit.upload is vyos_defined }} {% endfor %} {% endif %} {% endfor %} diff --git a/data/templates/chrony/chrony.conf.j2 b/data/templates/chrony/chrony.conf.j2 index 2838f5524..cc80e4d64 100644 --- a/data/templates/chrony/chrony.conf.j2 +++ b/data/templates/chrony/chrony.conf.j2 @@ -67,9 +67,9 @@ binddevice {{ interface }} {% endif %} {% endif %} -{% if ptp.timestamp.interface is vyos_defined %} +{% if timestamp.interface is vyos_defined %} # Enable hardware timestamping on the specified interfaces -{% for iface, iface_config in ptp.timestamp.interface.items() %} +{% for iface, iface_config in timestamp.interface.items() %} {% if iface == "all" %} {% set iface = "*" %} {% endif %} diff --git a/debian/rules b/debian/rules index df1d9e7f3..c15fcab11 100755 --- a/debian/rules +++ b/debian/rules @@ -9,6 +9,7 @@ VYOS_CFG_TMPL_DIR := opt/vyatta/share/vyatta-cfg/templates VYOS_OP_TMPL_DIR := opt/vyatta/share/vyatta-op/templates VYOS_MIBS_DIR := usr/share/snmp/mibs VYOS_LOCALUI_DIR := srv/localui +VYCONF_CONFIG_DIR := $(VYOS_LIBEXEC_DIR)/vyconf/config MIGRATION_SCRIPTS_DIR := opt/vyatta/etc/config-migrate/migrate ACTIVATION_SCRIPTS_DIR := usr/libexec/vyos/activate @@ -89,6 +90,8 @@ override_dh_auto_install: cp -r templates-op/* $(DIR)/$(VYOS_OP_TMPL_DIR) # Install data files + mkdir -p $(DIR)/$(VYCONF_CONFIG_DIR) + cp -r data/reftree.cache $(DIR)/$(VYCONF_CONFIG_DIR) mkdir -p $(DIR)/$(VYOS_DATA_DIR) cp -r data/* $(DIR)/$(VYOS_DATA_DIR) diff --git a/debian/vyos-1x.install b/debian/vyos-1x.install index 502fc7aaa..d5dd3bcec 100644 --- a/debian/vyos-1x.install +++ b/debian/vyos-1x.install @@ -40,6 +40,7 @@ usr/libexec/vyos/op_mode usr/libexec/vyos/services usr/libexec/vyos/system usr/libexec/vyos/validators +usr/libexec/vyos/vyconf usr/libexec/vyos/*.py usr/libexec/vyos/*.sh usr/share diff --git a/interface-definitions/container.xml.in b/interface-definitions/container.xml.in index bd2ff820d..ad1815604 100644 --- a/interface-definitions/container.xml.in +++ b/interface-definitions/container.xml.in @@ -275,6 +275,7 @@ </properties> <defaultValue>64</defaultValue> </leafNode> + #include <include/name-server-ipv4-ipv6.xml.i> <tagNode name="network"> <properties> <help>Attach user defined network to container</help> diff --git a/interface-definitions/include/interface/default-route-distance.xml.i b/interface-definitions/include/interface/default-route-distance.xml.i index 6eda52c91..7a226a538 100644 --- a/interface-definitions/include/interface/default-route-distance.xml.i +++ b/interface-definitions/include/interface/default-route-distance.xml.i @@ -4,7 +4,7 @@ <help>Distance for installed default route</help> <valueHelp> <format>u32:1-255</format> - <description>Distance for the default route from DHCP server</description> + <description>Distance for the default route received from the server</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-255"/> diff --git a/interface-definitions/interfaces_pppoe.xml.in b/interface-definitions/interfaces_pppoe.xml.in index 56660bc15..f24bc41d8 100644 --- a/interface-definitions/interfaces_pppoe.xml.in +++ b/interface-definitions/interfaces_pppoe.xml.in @@ -21,6 +21,9 @@ #include <include/interface/dial-on-demand.xml.i> #include <include/interface/no-default-route.xml.i> #include <include/interface/default-route-distance.xml.i> + <leafNode name="default-route-distance"> + <defaultValue>1</defaultValue> + </leafNode> #include <include/interface/dhcpv6-options.xml.i> #include <include/generic-description.xml.i> #include <include/interface/disable.xml.i> diff --git a/interface-definitions/pki.xml.in b/interface-definitions/pki.xml.in index b922771c1..5c0b735ef 100644 --- a/interface-definitions/pki.xml.in +++ b/interface-definitions/pki.xml.in @@ -35,6 +35,12 @@ <multi/> </properties> </leafNode> + <leafNode name="system-install"> + <properties> + <help>Install into CA certificate store on router</help> + <valueless/> + </properties> + </leafNode> #include <include/pki/cli-revoke.xml.i> </children> </tagNode> @@ -74,7 +80,7 @@ </constraint> </properties> </leafNode> - #include <include/listen-address-ipv4-single.xml.i> + #include <include/listen-address-single.xml.i> <leafNode name="rsa-key-size"> <properties> <help>Size of the RSA key</help> diff --git a/interface-definitions/service_ipoe-server.xml.in b/interface-definitions/service_ipoe-server.xml.in index 39cfb7889..6cc4471af 100644 --- a/interface-definitions/service_ipoe-server.xml.in +++ b/interface-definitions/service_ipoe-server.xml.in @@ -70,6 +70,18 @@ <constraintErrorMessage>VLAN IDs need to be in range 1-4094</constraintErrorMessage> </properties> </leafNode> + <leafNode name="static-ip"> + <properties> + <help>Static client IP address</help> + <valueHelp> + <format>ipv4</format> + <description>IPv4 address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + </leafNode> </children> </tagNode> </children> diff --git a/interface-definitions/service_ntp.xml.in b/interface-definitions/service_ntp.xml.in index 5dc0cd295..c31b572bd 100644 --- a/interface-definitions/service_ntp.xml.in +++ b/interface-definitions/service_ntp.xml.in @@ -13,72 +13,72 @@ #include <include/generic-interface.xml.i> #include <include/listen-address.xml.i> #include <include/interface/vrf.xml.i> - <node name="ptp"> + <node name="timestamp"> <properties> - <help>Enable Precision Time Protocol (PTP) transport</help> + <help>Enable timestamping of packets in the NIC hardware</help> </properties> <children> - #include <include/port-number.xml.i> - <leafNode name="port"> - <defaultValue>319</defaultValue> - </leafNode> - <node name="timestamp"> + <tagNode name="interface"> <properties> - <help>Enable timestamping of packets in the NIC hardware</help> + <help>Interface to enable timestamping on</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces</script> + <list>all</list> + </completionHelp> + <valueHelp> + <format>all</format> + <description>Select all interfaces</description> + </valueHelp> + <valueHelp> + <format>txt</format> + <description>Interface name</description> + </valueHelp> + <constraint> + #include <include/constraint/interface-name.xml.i> + <regex>all</regex> + </constraint> </properties> <children> - <tagNode name="interface"> + <leafNode name="receive-filter"> <properties> - <help>Interface to enable timestamping on</help> + <help>Selects which inbound packets are timestamped by the NIC</help> <completionHelp> - <script>${vyos_completion_dir}/list_interfaces</script> - <list>all</list> + <list>all ntp ptp none</list> </completionHelp> <valueHelp> <format>all</format> - <description>Select all interfaces</description> + <description>All packets are timestamped</description> </valueHelp> <valueHelp> - <format>txt</format> - <description>Interface name</description> + <format>ntp</format> + <description>Only NTP packets are timestamped</description> + </valueHelp> + <valueHelp> + <format>ptp</format> + <description>Only PTP or NTP packets using the PTP transport are timestamped</description> + </valueHelp> + <valueHelp> + <format>none</format> + <description>No packet is timestamped</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.i> - <regex>all</regex> + <regex>(all|ntp|ptp|none)</regex> </constraint> </properties> - <children> - <leafNode name="receive-filter"> - <properties> - <help>Selects which inbound packets are timestamped by the NIC</help> - <completionHelp> - <list>all ntp ptp none</list> - </completionHelp> - <valueHelp> - <format>all</format> - <description>All packets are timestamped</description> - </valueHelp> - <valueHelp> - <format>ntp</format> - <description>Only NTP packets are timestamped</description> - </valueHelp> - <valueHelp> - <format>ptp</format> - <description>Only PTP or NTP packets using the PTP transport are timestamped</description> - </valueHelp> - <valueHelp> - <format>none</format> - <description>No packet is timestamped</description> - </valueHelp> - <constraint> - <regex>(all|ntp|ptp|none)</regex> - </constraint> - </properties> - </leafNode> - </children> - </tagNode> + </leafNode> </children> - </node> + </tagNode> + </children> + </node> + <node name="ptp"> + <properties> + <help>Enable Precision Time Protocol (PTP) transport</help> + </properties> + <children> + #include <include/port-number.xml.i> + <leafNode name="port"> + <defaultValue>319</defaultValue> + </leafNode> </children> </node> <leafNode name="leap-second"> diff --git a/op-mode-definitions/show-bridge.xml.in b/op-mode-definitions/show-bridge.xml.in index 5d8cc3847..1212ab1f9 100644 --- a/op-mode-definitions/show-bridge.xml.in +++ b/op-mode-definitions/show-bridge.xml.in @@ -66,7 +66,7 @@ <properties> <help>Display bridge interface nexthop-group</help> </properties> - <command>${vyos_op_scripts_dir}/bridge.py show_detail --nexthop_group --interface=$3</command> + <command>${vyos_op_scripts_dir}/bridge.py show_detail --nexthop-group --interface=$3</command> </leafNode> </children> </tagNode> diff --git a/op-mode-definitions/show-license.xml.in b/op-mode-definitions/show-license.xml.in index 2ce11567d..45a0a9216 100644 --- a/op-mode-definitions/show-license.xml.in +++ b/op-mode-definitions/show-license.xml.in @@ -6,7 +6,7 @@ <properties> <help>Show VyOS license information</help> </properties> - <command>less $_vyatta_less_options --prompt=".license, page %dt of %D" -- ${vyatta_sysconfdir}/LICENSE</command> + <command>less $_vyatta_less_options --prompt=".license, page %dt of %D" -- ${vyos_data_dir}/EULA</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-monitoring.xml.in b/op-mode-definitions/show-monitoring.xml.in index 2651b3438..9fd60e45a 100644 --- a/op-mode-definitions/show-monitoring.xml.in +++ b/op-mode-definitions/show-monitoring.xml.in @@ -2,12 +2,68 @@ <interfaceDefinition> <node name="show"> <children> - <leafNode name="monitoring"> + <node name="monitoring"> <properties> <help>Show currently monitored services</help> </properties> - <command>vtysh -c "show debugging"</command> - </leafNode> + <children> + <node name="frr"> + <properties> + <help>Show currently monitored FRR services</help> + </properties> + <command>vtysh -c "show debugging"</command> + <children> + <node name="zebra"> + <properties> + <help>Show Zebra routing information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh "show ${@:4}"</command> + <children> + <node name="client"> + <properties> + <help>Client information</help> + </properties> + <children> + <node name="summary"> + <properties> + <help>Brief summary</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh "show ${@:4}"</command> + </node> + </children> + </node> + <node name="dplane"> + <properties> + <help>Zebra dataplane information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh "show ${@:4}"</command> + </node> + <node name="router"> + <properties> + <help>Zebra router information</help> + </properties> + <children> + <node name="table"> + <properties> + <help>Zebra routing table information</help> + </properties> + <children> + <node name="summary"> + <properties> + <help>Summary information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh "show ${@:4}"</command> + </node> + </children> + </node> + </children> + </node> + </children> + </node> + </children> + </node> + </children> + </node> </children> </node> </interfaceDefinition> diff --git a/op-mode-definitions/show-zebra.xml.in b/op-mode-definitions/show-zebra.xml.in deleted file mode 100644 index 69991a1d5..000000000 --- a/op-mode-definitions/show-zebra.xml.in +++ /dev/null @@ -1,54 +0,0 @@ -<?xml version="1.0"?> -<interfaceDefinition> - <node name="show"> - <children> - <node name="zebra"> - <properties> - <help>Show Zebra routing information</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - <children> - <node name="client"> - <properties> - <help>Client information </help> - </properties> - <children> - <node name="summary"> - <properties> - <help>Brief Summary</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </node> - </children> - </node> - <node name="dplane"> - <properties> - <help>Zebra dataplane information</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </node> - <node name="router"> - <properties> - <help>Zebra Router Information</help> - </properties> - <children> - <node name="table"> - <properties> - <help>Table Information about this Zebra Router</help> - </properties> - <children> - <node name="summary"> - <properties> - <help>Summary Information</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </node> - </children> - </node> - </children> - </node> - </children> - </node> - </children> - </node> -</interfaceDefinition> diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py index 90b96b88c..dd3ad1e3d 100644 --- a/python/vyos/configsession.py +++ b/python/vyos/configsession.py @@ -23,8 +23,8 @@ from vyos.utils.process import is_systemd_service_running from vyos.utils.dict import dict_to_paths CLI_SHELL_API = '/bin/cli-shell-api' -SET = '/opt/vyatta/sbin/my_set' -DELETE = '/opt/vyatta/sbin/my_delete' +SET = '/usr/libexec/vyos/vyconf/vy_set' +DELETE = '/usr/libexec/vyos/vyconf/vy_delete' COMMENT = '/opt/vyatta/sbin/my_comment' COMMIT = '/opt/vyatta/sbin/my_commit' DISCARD = '/opt/vyatta/sbin/my_discard' diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py index 3e02fbba6..fb79e8459 100644 --- a/python/vyos/configtree.py +++ b/python/vyos/configtree.py @@ -469,15 +469,15 @@ def mask_inclusive(left, right, libpath=LIBPATH): return tree -def reference_tree_to_json(from_dir, to_file, libpath=LIBPATH): +def reference_tree_to_json(from_dir, to_file, internal_cache="", libpath=LIBPATH): try: __lib = cdll.LoadLibrary(libpath) __reference_tree_to_json = __lib.reference_tree_to_json - __reference_tree_to_json.argtypes = [c_char_p, c_char_p] + __reference_tree_to_json.argtypes = [c_char_p, c_char_p, c_char_p] __get_error = __lib.get_error __get_error.argtypes = [] __get_error.restype = c_char_p - res = __reference_tree_to_json(from_dir.encode(), to_file.encode()) + res = __reference_tree_to_json(internal_cache.encode(), from_dir.encode(), to_file.encode()) except Exception as e: raise ConfigTreeError(e) if res == 1: diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py index dec619d3e..425990967 100644 --- a/python/vyos/defaults.py +++ b/python/vyos/defaults.py @@ -36,7 +36,8 @@ directories = { 'isc_dhclient_dir' : '/run/dhclient', 'dhcp6_client_dir' : '/run/dhcp6c', 'vyos_configdir' : '/opt/vyatta/config', - 'completion_dir' : f'{base_dir}/completion' + 'completion_dir' : f'{base_dir}/completion', + 'ca_certificates' : '/usr/local/share/ca-certificates/vyos' } config_status = '/tmp/vyos-config-status' diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 61da7b74b..50dd0f396 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -310,13 +310,15 @@ class EthernetIf(Interface): rps_cpus = 0 queues = len(glob(f'/sys/class/net/{self.ifname}/queues/rx-*')) if state: + cpu_count = os.cpu_count() + # Enable RPS on all available CPUs except CPU0 which we will not # utilize so the system has one spare core when it's under high # preasure to server other means. Linux sysfs excepts a bitmask # representation of the CPUs which should participate on RPS, we # can enable more CPUs that are physically present on the system, # Linux will clip that internally! - rps_cpus = (1 << os.cpu_count()) -1 + rps_cpus = (1 << cpu_count) - 1 # XXX: we should probably reserve one core when the system is under # high preasure so we can still have a core left for housekeeping. @@ -324,8 +326,19 @@ class EthernetIf(Interface): # receive packet steering. rps_cpus &= ~1 - for i in range(0, queues): - self._write_sysfs(f'/sys/class/net/{self.ifname}/queues/rx-{i}/rps_cpus', f'{rps_cpus:x}') + # Convert the bitmask to hexadecimal chunks of 32 bits + # Split the bitmask into chunks of up to 32 bits each + hex_chunks = [] + for i in range(0, cpu_count, 32): + # Extract the next 32-bit chunk + chunk = (rps_cpus >> i) & 0xFFFFFFFF + hex_chunks.append(f"{chunk:08x}") + + # Join the chunks with commas + rps_cpus = ",".join(hex_chunks) + + for i in range(queues): + self._write_sysfs(f'/sys/class/net/{self.ifname}/queues/rx-{i}/rps_cpus', rps_cpus) # send bitmask representation as hex string without leading '0x' return True diff --git a/python/vyos/utils/misc.py b/python/vyos/utils/misc.py index d82655914..ac8011b8d 100644 --- a/python/vyos/utils/misc.py +++ b/python/vyos/utils/misc.py @@ -52,7 +52,7 @@ def install_into_config(conf, config_paths, override_prompt=True): continue try: - cmd(f'/opt/vyatta/sbin/my_set {path}') + cmd(f'/usr/libexec/vyos/vyconf/vy_set {path}') count += 1 except: failed.append(path) diff --git a/python/vyos/xml_ref/generate_cache.py b/python/vyos/xml_ref/generate_cache.py index 5f3f84dee..093697993 100755 --- a/python/vyos/xml_ref/generate_cache.py +++ b/python/vyos/xml_ref/generate_cache.py @@ -55,6 +55,8 @@ def main(): parser = ArgumentParser(description='generate and save dict from xml defintions') parser.add_argument('--xml-dir', type=str, required=True, help='transcluded xml interface-definition directory') + parser.add_argument('--internal-cache', type=str, required=True, + help='cache as unrendered json data for loading by vyconfd') parser.add_argument('--package-name', type=non_trivial, default='vyos-1x', help='name of current package') parser.add_argument('--output-path', help='path to generated cache') @@ -66,9 +68,11 @@ def main(): out_path = args['output_path'] path = out_path if out_path is not None else pkg_cache xml_cache = abspath(join(path, cache_name)) + internal_cache = args['internal_cache'] try: - reference_tree_to_json(xml_dir, xml_tmp) + reference_tree_to_json(xml_dir, xml_tmp, + internal_cache=internal_cache) except ConfigTreeError as e: print(e) sys.exit(1) diff --git a/smoketest/scripts/cli/test_container.py b/smoketest/scripts/cli/test_container.py index 0541384da..51559a7c6 100755 --- a/smoketest/scripts/cli/test_container.py +++ b/smoketest/scripts/cli/test_container.py @@ -96,6 +96,31 @@ class TestContainer(VyOSUnitTestSHIM.TestCase): tmp = cmd(f'sudo podman exec -it {cont_name} sysctl kernel.msgmax') self.assertEqual(tmp, 'kernel.msgmax = 4096') + def test_name_server(self): + cont_name = 'dns-test' + net_name = 'net-test' + name_server = '192.168.0.1' + prefix = '192.0.2.0/24' + + self.cli_set(base_path + ['network', net_name, 'prefix', prefix]) + + self.cli_set(base_path + ['name', cont_name, 'image', cont_image]) + self.cli_set(base_path + ['name', cont_name, 'name-server', name_server]) + self.cli_set(base_path + ['name', cont_name, 'network', net_name, 'address', str(ip_interface(prefix).ip + 2)]) + + # verify() - name server has no effect when container network has dns enabled + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + self.cli_set(base_path + ['network', net_name, 'no-name-server']) + self.cli_commit() + + n = cmd_to_json(f'sudo podman inspect {cont_name}') + self.assertEqual(n['HostConfig']['Dns'][0], name_server) + + tmp = cmd(f'sudo podman exec -it {cont_name} cat /etc/resolv.conf') + self.assertIn(name_server, tmp) + def test_cpu_limit(self): cont_name = 'c2' diff --git a/smoketest/scripts/cli/test_service_ipoe-server.py b/smoketest/scripts/cli/test_service_ipoe-server.py index be03179bf..ab0898d17 100755 --- a/smoketest/scripts/cli/test_service_ipoe-server.py +++ b/smoketest/scripts/cli/test_service_ipoe-server.py @@ -260,6 +260,41 @@ delegate={delegate_2_prefix},{delegate_mask},name={pool_name}""" tmp = ','.join(vlans) self.assertIn(f'{interface},{tmp}', conf['ipoe']['vlan-mon']) + def test_ipoe_server_static_client_ip(self): + mac_address = '08:00:27:2f:d8:06' + ip_address = '192.0.2.100' + + # Test configuration of local authentication for PPPoE server + self.basic_config() + # Rewrite authentication from basic_config + self.set( + [ + 'authentication', + 'interface', + interface, + 'mac', + mac_address, + 'static-ip', + ip_address, + ] + ) + self.set(['authentication', 'mode', 'local']) + # commit changes + self.cli_commit() + + # Validate configuration values + conf = ConfigParser(allow_no_value=True, delimiters='=', strict=False) + conf.read(self._config_file) + + # basic verification + self.verify(conf) + + # check local users + tmp = cmd(f'sudo cat {self._chap_secrets}') + regex = f'{interface}\s+\*\s+{mac_address}\s+{ip_address}' + tmp = re.findall(regex, tmp) + self.assertTrue(tmp) + @unittest.skip("PPP is not a part of IPoE") def test_accel_ppp_options(self): pass diff --git a/smoketest/scripts/cli/test_service_ntp.py b/smoketest/scripts/cli/test_service_ntp.py index 07af4f5eb..469d44eaa 100755 --- a/smoketest/scripts/cli/test_service_ntp.py +++ b/smoketest/scripts/cli/test_service_ntp.py @@ -203,7 +203,7 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['server', server, 'ptp']) self.cli_set(base_path + ['ptp', 'port', ptp_port]) - self.cli_set(base_path + ['ptp', 'timestamp', 'interface', 'all']) + self.cli_set(base_path + ['timestamp', 'interface', 'all']) # commit changes self.cli_commit() diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py index a7dc33d9d..594de3eb0 100755 --- a/src/conf_mode/container.py +++ b/src/conf_mode/container.py @@ -148,6 +148,9 @@ def verify(container): if network_name not in container.get('network', {}): raise ConfigError(f'Container network "{network_name}" does not exist!') + if 'name_server' in container_config and 'no_name_server' not in container['network'][network_name]: + raise ConfigError(f'Setting name server has no effect when attached container network has DNS enabled!') + if 'address' in container_config['network'][network_name]: cnt_ipv4 = 0 cnt_ipv6 = 0 @@ -363,9 +366,14 @@ def generate_run_arguments(name, container_config): if 'allow_host_pid' in container_config: host_pid = '--pid host' + name_server = '' + if 'name_server' in container_config: + for ns in container_config['name_server']: + name_server += f'--dns {ns}' + 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}' + f'--name {name} {hostname} {device} {port} {name_server} {volume} {env_opt} {label} {uid} {host_pid}' entrypoint = '' if 'entrypoint' in container_config: diff --git a/src/conf_mode/pki.py b/src/conf_mode/pki.py index 45e0129a3..acea2c9be 100755 --- a/src/conf_mode/pki.py +++ b/src/conf_mode/pki.py @@ -50,6 +50,7 @@ from vyos import airbag airbag.enable() vyos_certbot_dir = directories['certbot'] +vyos_ca_certificates_dir = directories['ca_certificates'] # keys to recursively search for under specified path sync_search = [ @@ -149,35 +150,15 @@ def get_config(config=None): if len(argv) > 1 and argv[1] == 'certbot_renew': pki['certbot_renew'] = {} - tmp = node_changed(conf, base + ['ca'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD) - if tmp: - if 'changed' not in pki: pki.update({'changed':{}}) - pki['changed'].update({'ca' : tmp}) - - tmp = node_changed(conf, base + ['certificate'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD) - if tmp: - if 'changed' not in pki: pki.update({'changed':{}}) - pki['changed'].update({'certificate' : tmp}) + changed_keys = ['ca', 'certificate', 'dh', 'key-pair', 'openssh', 'openvpn'] - tmp = node_changed(conf, base + ['dh'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD) - if tmp: - if 'changed' not in pki: pki.update({'changed':{}}) - pki['changed'].update({'dh' : tmp}) + for key in changed_keys: + tmp = node_changed(conf, base + [key], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD) - tmp = node_changed(conf, base + ['key-pair'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD) - if tmp: - if 'changed' not in pki: pki.update({'changed':{}}) - pki['changed'].update({'key_pair' : tmp}) - - tmp = node_changed(conf, base + ['openssh'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD) - if tmp: - if 'changed' not in pki: pki.update({'changed':{}}) - pki['changed'].update({'openssh' : tmp}) + if 'changed' not in pki: + pki.update({'changed':{}}) - tmp = node_changed(conf, base + ['openvpn', 'shared-secret'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD) - if tmp: - if 'changed' not in pki: pki.update({'changed':{}}) - pki['changed'].update({'openvpn' : tmp}) + pki['changed'].update({key.replace('-', '_') : tmp}) # We only merge on the defaults of there is a configuration at all if conf.exists(base): @@ -417,10 +398,33 @@ def verify(pki): return None +def cleanup_system_ca(): + if not os.path.exists(vyos_ca_certificates_dir): + os.mkdir(vyos_ca_certificates_dir) + else: + for filename in os.listdir(vyos_ca_certificates_dir): + full_path = os.path.join(vyos_ca_certificates_dir, filename) + if os.path.isfile(full_path): + os.unlink(full_path) + def generate(pki): if not pki: + cleanup_system_ca() return None + # Create or cleanup CA install directory + if 'changed' in pki and 'ca' in pki['changed']: + cleanup_system_ca() + + if 'ca' in pki: + for ca, ca_conf in pki['ca'].items(): + if 'system_install' in ca_conf: + ca_obj = load_certificate(ca_conf['certificate']) + ca_path = os.path.join(vyos_ca_certificates_dir, f'{ca}.crt') + + with open(ca_path, 'w') as f: + f.write(encode_certificate(ca_obj)) + # Certbot renewal only needs to re-trigger the services to load up the # new PEM file if 'certbot_renew' in pki: @@ -487,6 +491,7 @@ def apply(pki): systemd_certbot_name = 'certbot.timer' if not pki: call(f'systemctl stop {systemd_certbot_name}') + call('update-ca-certificates') return None has_certbot = False @@ -504,6 +509,10 @@ def apply(pki): if 'changed' in pki: call_dependents() + # Rebuild ca-certificates bundle + if 'ca' in pki['changed']: + call('update-ca-certificates') + return None if __name__ == '__main__': diff --git a/src/conf_mode/system_config-management.py b/src/conf_mode/system_config-management.py index 8de4e5342..a3ce66512 100755 --- a/src/conf_mode/system_config-management.py +++ b/src/conf_mode/system_config-management.py @@ -39,6 +39,9 @@ def get_config(config=None): def verify(mgmt): + if mgmt is None: + return + d = mgmt.config_dict confirm = d.get('commit_confirm', {}) if confirm.get('action', '') == 'reload' and 'commit_revisions' not in d: diff --git a/src/etc/sudoers.d/vyos b/src/etc/sudoers.d/vyos index 67d7babc4..198b9b9aa 100644 --- a/src/etc/sudoers.d/vyos +++ b/src/etc/sudoers.d/vyos @@ -1,7 +1,8 @@ # # VyOS modifications to sudo configuration # -Defaults syslog_goodpri=info +Defaults !syslog +Defaults !pam_session Defaults env_keep+=VYATTA_* # diff --git a/src/init/vyos-router b/src/init/vyos-router index 8825cc16a..e2e964656 100755 --- a/src/init/vyos-router +++ b/src/init/vyos-router @@ -24,6 +24,8 @@ declare action=$1; shift declare -x BOOTFILE=$vyatta_sysconfdir/config/config.boot declare -x DEFAULT_BOOTFILE=$vyatta_sysconfdir/config.boot.default +declare -x VYCONF_CONFIG_DIR=/usr/libexec/vyos/vyconf/config + # If vyos-config= boot option is present, use that file instead for x in $(cat /proc/cmdline); do [[ $x = vyos-config=* ]] || continue @@ -146,6 +148,10 @@ init_bootfile () { chgrp ${GROUP} $BOOTFILE chmod 660 $BOOTFILE fi + if [ -d $VYCONF_CONFIG_DIR ] ; then + cp -f $BOOTFILE $VYCONF_CONFIG_DIR/config.boot + cp -f $DEFAULT_BOOTFILE $VYCONF_CONFIG_DIR/config.failsafe + fi } # if necessary, migrate initial config @@ -154,6 +160,10 @@ migrate_bootfile () if [ -x $vyos_libexec_dir/run-config-migration.py ]; then log_progress_msg migrate sg ${GROUP} -c "$vyos_libexec_dir/run-config-migration.py $BOOTFILE" + # update vyconf copy after migration + if [ -d $VYCONF_CONFIG_DIR ] ; then + cp -f $BOOTFILE $VYCONF_CONFIG_DIR/config.boot + fi fi } @@ -471,6 +481,12 @@ start () touch /tmp/vyos.smoketest.debug fi + # Cleanup PKI CAs + if [ -d /usr/local/share/ca-certificates/vyos ]; then + rm -f /usr/local/share/ca-certificates/vyos/*.crt + update-ca-certificates >/dev/null 2>&1 + fi + log_action_begin_msg "Mounting VyOS Config" # ensure the vyatta_configdir supports a large number of inodes since # the config hierarchy is often inode-bound (instead of size). @@ -512,6 +528,8 @@ start () disabled system_config || system_config + systemctl start vyconfd.service + for s in ${subinit[@]} ; do if ! disabled $s; then log_progress_msg $s @@ -554,6 +572,8 @@ stop() umount ${vyatta_configdir} log_action_end_msg $? + systemctl stop vyconfd.service + systemctl stop frr.service unmount_encrypted_config diff --git a/src/op_mode/bridge.py b/src/op_mode/bridge.py index e80b1c21d..c4293a77c 100755 --- a/src/op_mode/bridge.py +++ b/src/op_mode/bridge.py @@ -23,10 +23,11 @@ from tabulate import tabulate from vyos.utils.process import cmd from vyos.utils.process import rc_cmd -from vyos.utils.process import call +from vyos.utils.process import call import vyos.opmode + def _get_json_data(): """ Get bridge data format JSON @@ -43,7 +44,7 @@ def _get_raw_data_summary(): return data_dict -def _get_raw_data_vlan(tunnel:bool=False): +def _get_raw_data_vlan(tunnel: bool = False): """ :returns dict """ @@ -54,14 +55,18 @@ def _get_raw_data_vlan(tunnel:bool=False): data_dict = json.loads(json_data) return data_dict + def _get_raw_data_vni() -> dict: """ :returns dict """ - json_data = cmd(f'bridge --json vni show') + code, json_data = rc_cmd(f'bridge --json vni show') + if code != 0: + raise vyos.opmode.UnconfiguredObject('VNI is not configured') data_dict = json.loads(json_data) return data_dict + def _get_raw_data_fdb(bridge): """Get MAC-address for the bridge brX :returns list @@ -70,7 +75,9 @@ def _get_raw_data_fdb(bridge): # From iproute2 fdb.c, fdb_show() will only exit(-1) in case of # non-existent bridge device; raise error. if code == 255: - raise vyos.opmode.UnconfiguredObject(f"bridge {bridge} does not exist in the system") + raise vyos.opmode.UnconfiguredObject( + f'bridge {bridge} does not exist in the system' + ) data_dict = json.loads(json_data) return data_dict @@ -116,8 +123,8 @@ def _get_formatted_output_summary(data): flags = ','.join(option.get('flags')).lower() prio = option.get('priority') member_entries.append([interface, state, mtu, flags, prio]) - member_headers = ["Member", "State", "MTU", "Flags", "Prio"] - output_members = tabulate(member_entries, member_headers, numalign="left") + member_headers = ['Member', 'State', 'MTU', 'Flags', 'Prio'] + output_members = tabulate(member_entries, member_headers, numalign='left') output_bridge = f"""Bridge interface {bridge}: {output_members} @@ -138,13 +145,14 @@ def _get_formatted_output_vlan(data): vlan_end = vlan_entry.get('vlanEnd') vlan = f'{vlan}-{vlan_end}' flags_raw = vlan_entry.get('flags') - flags = ', '.join(flags_raw if isinstance(flags_raw,list) else "").lower() + flags = ', '.join(flags_raw if isinstance(flags_raw, list) else '').lower() data_entries.append([interface, vlan, flags]) - headers = ["Interface", "VLAN", "Flags"] + headers = ['Interface', 'VLAN', 'Flags'] output = tabulate(data_entries, headers) return output + def _get_formatted_output_vlan_tunnel(data): data_entries = [] for entry in data: @@ -166,10 +174,11 @@ def _get_formatted_output_vlan_tunnel(data): # 200 200 data_entries.append(['', vlan, vni]) - headers = ["Interface", "VLAN", "VNI"] + headers = ['Interface', 'VLAN', 'VNI'] output = tabulate(data_entries, headers) return output + def _get_formatted_output_vni(data): data_entries = [] for entry in data: @@ -182,10 +191,11 @@ def _get_formatted_output_vni(data): vlan = f'{vlan}-{vlan_end}' data_entries.append([interface, vlan]) - headers = ["Interface", "VNI"] + headers = ['Interface', 'VNI'] output = tabulate(data_entries, headers) return output + def _get_formatted_output_fdb(data): data_entries = [] for entry in data: @@ -195,8 +205,8 @@ def _get_formatted_output_fdb(data): flags = ','.join(entry['flags']) data_entries.append([interface, mac, state, flags]) - headers = ["Interface", "Mac address", "State", "Flags"] - output = tabulate(data_entries, headers, numalign="left") + headers = ['Interface', 'Mac address', 'State', 'Flags'] + output = tabulate(data_entries, headers, numalign='left') return output @@ -209,28 +219,33 @@ def _get_formatted_output_mdb(data): state = mdb_entry.get('state') flags = ','.join(mdb_entry.get('flags')) data_entries.append([interface, group, state, flags]) - headers = ["Interface", "Group", "State", "Flags"] + headers = ['Interface', 'Group', 'State', 'Flags'] output = tabulate(data_entries, headers) return output + def _get_bridge_detail(iface): """Get interface detail statistics""" return call(f'vtysh -c "show interface {iface}"') + def _get_bridge_detail_nexthop_group(iface): """Get interface detail nexthop_group statistics""" return call(f'vtysh -c "show interface {iface} nexthop-group"') + def _get_bridge_detail_nexthop_group_raw(iface): out = cmd(f'vtysh -c "show interface {iface} nexthop-group"') return out + def _get_bridge_detail_raw(iface): """Get interface detail json statistics""" - data = cmd(f'vtysh -c "show interface {iface} json"') + data = cmd(f'vtysh -c "show interface {iface} json"') data_dict = json.loads(data) return data_dict + def show(raw: bool): bridge_data = _get_raw_data_summary() if raw: @@ -249,6 +264,7 @@ def show_vlan(raw: bool, tunnel: typing.Optional[bool]): else: return _get_formatted_output_vlan(bridge_vlan) + def show_vni(raw: bool): bridge_vni = _get_raw_data_vni() if raw: @@ -256,6 +272,7 @@ def show_vni(raw: bool): else: return _get_formatted_output_vni(bridge_vni) + def show_fdb(raw: bool, interface: str): fdb_data = _get_raw_data_fdb(interface) if raw: @@ -271,6 +288,7 @@ def show_mdb(raw: bool, interface: str): else: return _get_formatted_output_mdb(mdb_data) + def show_detail(raw: bool, nexthop_group: typing.Optional[bool], interface: str): if raw: if nexthop_group: @@ -283,6 +301,7 @@ def show_detail(raw: bool, nexthop_group: typing.Optional[bool], interface: str) else: return _get_bridge_detail(interface) + if __name__ == '__main__': try: res = vyos.opmode.run(sys.modules[__name__]) diff --git a/src/systemd/vyconfd.service b/src/systemd/vyconfd.service new file mode 100644 index 000000000..ab2280263 --- /dev/null +++ b/src/systemd/vyconfd.service @@ -0,0 +1,21 @@ +[Unit] +Description=VyOS vyconf daemon + +# Without this option, lots of default dependencies are added, +# among them network.target, which creates a dependency cycle +DefaultDependencies=no + +After=systemd-remount-fs.service + +[Service] +ExecStart=/usr/libexec/vyos/vyconf/vyconfd --log-file /var/run/log/vyconfd.log +Type=exec +SyslogIdentifier=vyconfd +SyslogFacility=daemon +Restart=on-failure + +User=root +Group=vyattacfg + +[Install] +WantedBy=vyos.target |