diff options
Diffstat (limited to 'src/etc')
76 files changed, 3547 insertions, 0 deletions
diff --git a/src/etc/bash_completion.d/vyatta-op b/src/etc/bash_completion.d/vyatta-op new file mode 100644 index 0000000..8ac2d9b --- /dev/null +++ b/src/etc/bash_completion.d/vyatta-op @@ -0,0 +1,685 @@ +# vyatta bash operational mode completion +# **** License **** +# 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 +# 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. +# +# This code was originally developed by Vyatta, Inc. +# Portions created by Vyatta are Copyright (C) 2006, 2007 Vyatta, Inc. +# All Rights Reserved. +# +# Author: Tom Grennan +# Date: 2007 +# Description: setup bash completion for Vyatta operational commands +# +# **** End License **** + +test -z "$_vyatta_less_options" && \ + declare -r _vyatta_less_options="\ + --QUIT-AT-EOF\ + --quit-if-one-screen\ + --RAW-CONTROL-CHARS\ + --squeeze-blank-lines\ + --no-init" +test -z "$_vyatta_default_pager" && \ + declare -r _vyatta_default_pager="less \ + --buffers=64\ + --auto-buffers\ + --no-lessopen\ + $_vyatta_less_options" +test -z "$VYATTA_PAGER" && \ + declare -x VYATTA_PAGER=$_vyatta_default_pager + +_vyatta_op_do_key_bindings () +{ + if [[ "$SHELL" != "/bin/vbash" && "$SHELL" != "/sbin/radius_shell" ]]; then + # only do bindings if vbash and radius_shell + return + fi + nullglob_save=$(shopt -p nullglob) + shopt -u nullglob + case "$-" in + *i*) + bind '"?": possible-completions' + bind 'set show-all-if-ambiguous on' + bind_cmds=$(grep '^bind .* # vyatta key binding$' $HOME/.bashrc) + eval $bind_cmds + ;; + esac + eval $nullglob_save +} + +_vyatta_op_do_key_bindings + +test -f /etc/default/vyatta && \ + source /etc/default/vyatta + +test ! -d "$vyatta_op_templates" && \ + return 0 + +case "$-" in + *i*) + declare -r _vyatta_op_last_comp_init='>>>>>>LASTCOMP<<<<<<' + ;; +esac +declare _vyatta_op_last_comp=${_vyatta_op_last_comp_init} +declare _vyatta_op_node_path +declare -a _vyatta_op_noncompletions _vyatta_op_completions +declare -x -a _vyatta_pipe_noncompletions _vyatta_pipe_completions +declare _vyatta_comptype +declare -x -a reply +declare -a _vyatta_operator_allowed + +if [[ "$VYATTA_USER_LEVEL_DIR" != "/opt/vyatta/etc/shell/level/admin" ]]; then + _vyatta_operator_allowed=( $(cat $VYATTA_USER_LEVEL_DIR/allowed-op) ) +fi + +declare -a functions +functions=( /opt/vyatta/share/vyatta-op/functions/interpreter/* ) + +for file in "${functions[@]}";do + source $file; +done + +# $1: label +# #2...: strings +_vyatta_op_debug () +{ + echo -ne \\n$1: + shift + for s ; do + echo -ne " \"$s\"" + done +} + +# this is needed to provide original "default completion" behavior. +# see "vyatta-cfg" completion script for details. +_vyatta_op_default_expand () +{ + local wc=${#COMP_WORDS[@]} + if [[ "${COMP_WORDS[0]}" =~ "/" ]]; then + # if we are looking for a directory on the first completion then do directory completions + _filedir_xspec_vyos + elif (( wc < 2 )) || + [[ $COMP_CWORD -eq 0 ]] || + [[ $1 == $2 ]]; then + _vyatta_op_expand "$@" + else + # after the first word => cannot be vyatta command so use original default + _filedir_xspec_vyos + fi +} + +# $1: label +# $2...: help +_vyatta_op_print_help () +{ + local label=$1 help=$2 + if [ ${#label} -eq 0 ] ; then + return + elif [ ${#help} -eq 0 ] ; then + echo -ne "\n $label" + elif [ ${#label} -lt 6 ] ; then + echo -ne "\n $label\t\t\t$help" + elif [ ${#label} -lt 14 ] ; then + echo -ne "\n $label\t\t$help" + elif [ ${#label} -lt 21 ] ; then + echo -ne "\n $label\t$help" + else + echo -ne "\n $label\n\t\t\t$help" + fi +} + +# $1: $cur +# $2...: possible completions +_vyatta_op_help () +{ + local restore_shopts=$( shopt -p extglob nullglob | tr \\n \; ) + shopt -u nullglob + local cur=$1; shift + local ndef node_tag_help node_run help last_help + + ndef=${_vyatta_op_node_path}/node.tag/node.def + [ -f $ndef ] && \ + node_tag_help=$( _vyatta_op_get_node_def_field $ndef help ) + + ndef=${_vyatta_op_node_path}/node.def + [ -f $ndef ] && \ + node_run=$( _vyatta_op_get_node_def_field $ndef run ) + + if [[ "$1" == "<nocomps>" ]]; then + eval "$restore_shopts" + return + fi + echo -en "\nPossible completions:" + if [ -z "$cur" -a -n "$node_run" ]; then + _vyatta_op_print_help '<Enter>' "Execute the current command" + fi + if [ $# -eq 0 ];then + _vyatta_op_print_help '<text>' "$node_tag_help" + eval "$restore_shopts" + return + fi + for comp ; do + if [[ "$comp" == "<Enter>" ]]; then + continue + fi + if [ -z "$comp" ] ; then + if [ "X$node_tag_help" == "X$last_help" ] ; then + help="" + else + last_help=$node_tag_help + help=$node_tag_help + fi + _vyatta_op_print_help '*' "$help" + elif [[ -z "$cur" || $comp == ${cur}* ]] ; then + ndef=${_vyatta_op_node_path}/$comp/node.def + if [ -f $ndef ] ; then + help=$( _vyatta_op_get_node_def_field $ndef help ) + else + help=$node_tag_help + fi + if [ "X$help" == "X$last_help" ] ; then + help="" + else + last_help=$help + fi + _vyatta_op_print_help "$comp" "$help" + fi + done + eval "$restore_shopts" +} + +_vyatta_op_set_node_path () +{ + local node + _vyatta_op_node_path=$vyatta_op_templates + for (( i=0 ; i<COMP_CWORD ; i++ )) ; do + # expand the command so completion continues to work with short versions + if [[ "${COMP_WORDS[i]}" == "*" ]]; then + node="node.tag" # user defined wildcars are always tag nodes + else + node=$(_vyatta_op_conv_node_path $_vyatta_op_node_path ${COMP_WORDS[i]}) + fi + if [ -f "${_vyatta_op_node_path}/$node/node.def" ] ; then + _vyatta_op_node_path+=/$node + elif [ -f ${_vyatta_op_node_path}/node.tag/node.def ] ; then + _vyatta_op_node_path+=/node.tag + else + return 1 + fi + done +} + +_vyatta_op_set_completions () +{ + local -a allowed completions + local cur=$1 + local restore_shopts=$( shopt -p extglob nullglob | tr \\n \; ) + for ndef in ${_vyatta_op_node_path}/*/node.def ; do + if [[ $ndef == */node.tag/node.def ]] ; then + local acmd=$( _vyatta_op_get_node_def_field $ndef allowed ) + shopt -u extglob nullglob + local -a a=($( eval "$acmd" )) + eval "$restore_shopts" + + if [ ${#a[@]} -ne 0 ] ; then + allowed+=( "${a[@]}" ) + else + allowed+=( "<text>" ) + fi + else + local sdir=${ndef%/*} + allowed+=( ${sdir##*/} ) + fi + done + + # donot complete entries like <HOSTNAME> or <A.B.C.D> + _vyatta_op_noncompletions=( ) + completions=( ) + + # make runable commands have a non-comp + ndef=${_vyatta_op_node_path}/node.def + [ -f $ndef ] && \ + node_run=$( _vyatta_op_get_node_def_field $ndef run ) + if [ -z "$cur" -a -n "$node_run" ]; then + _vyatta_op_noncompletions+=('<Enter>') + fi + + for (( i=0 ; i<${#allowed[@]} ; i++ )) ; do + if [[ "${allowed[i]}" == \<*\> ]] ; then + _vyatta_op_noncompletions+=( "${allowed[i]}" ) + else + if [[ "$VYATTA_USER_LEVEL_DIR" == "/opt/vyatta/etc/shell/level/admin" ]]; then + completions+=( ${allowed[i]} ) + elif is_elem_of ${allowed[i]} _vyatta_operator_allowed; then + completions+=( ${allowed[i]} ) + elif [[ $_vyatta_op_node_path == $vyatta_op_templates ]];then + continue + else + completions+=( ${allowed[i]} ) + fi + fi + done + + # Prefix filter the non empty completions + if [ -n "$cur" ]; then + _vyatta_op_completions=() + get_prefix_filtered_list "$cur" completions _vyatta_op_completions + _vyatta_op_completions=($( printf "%s\n" ${_vyatta_op_completions[@]} | sort -u )) + else + _vyatta_op_completions=($( printf "%s\n" ${completions[@]} | sort -u )) + fi + #shopt -s nullglob +} + +_vyatta_op_comprely_needs_ambiguity () +{ + local -a uniq + + [ ${#COMPREPLY[@]} -eq 1 ] && return + + uniq=( `printf "%s\n" ${COMPREPLY[@]} | cut -c1 | sort -u` ) + + [ ${#uniq[@]} -eq 1 ] && return + false +} + +_vyatta_op_invalid_completion () +{ + local tpath=$vyatta_op_templates + local -a args + local i=1 + for arg in "${COMP_WORDS[@]}"; do + arg=( $(_vyatta_op_conv_node_path $tpath $arg) ) # expand the arguments + # output proper error message based on the above expansion + if [[ "${arg[1]}" == "ambiguous" ]]; then + echo -ne "\n\n Ambiguous command: ${args[@]} [$arg]\n" + local -a cmds=( $(compgen -d $tpath/$arg) ) + _vyatta_op_node_path=$tpath + local comps=$(_vyatta_op_help $arg ${cmds[@]##*/}) + echo -ne "$comps" | sed -e 's/^P/ P/' + break + elif [[ "${arg[1]}" == "invalid" ]]; then + echo -ne "\n\n Invalid command: ${args[@]} [$arg]" + break + fi + + if [ -f "$tpath/$arg/node.def" ] ; then + tpath+=/$arg + elif [ -f $tpath/node.tag/node.def ] ; then + tpath+=/node.tag + else + echo -ne "\n\n Invalid command: ${args[@]} [$arg]" >&2 + break + fi + args[$i]=$arg + let "i+=1" + if [ $[${#COMP_WORDS[@]}+1] -eq $i ];then + _vyatta_op_help "" \ + "${_vyatta_op_noncompletions[@]}" \ + "${_vyatta_op_completions[@]}" \ + | ${VYATTA_PAGER:-cat} + fi + done +} + +_vyatta_op_expand () +{ + # We need nospace here and we have to append our own spaces + compopt -o nospace + + local restore_shopts=$( shopt -p extglob nullglob | tr \\n \; ) + shopt -s extglob nullglob + local cur="" + local _has_comptype=0 + local current_prefix=$2 + local current_word=$3 + _vyatta_comptype="" + + if (( ${#COMP_WORDS[@]} > 0 )); then + cur=${COMP_WORDS[COMP_CWORD]} + else + (( COMP_CWORD = ${#COMP_WORDS[@]} )) + fi + + if _vyatta_pipe_completion "${COMP_WORDS[@]}"; then + if [ "${COMP_WORDS[*]}" == "$_vyatta_op_last_comp" ] || + [ ${#_vyatta_pipe_completions[@]} -eq 0 ]; then + _vyatta_do_pipe_help + COMPREPLY=( "" " " ) + _vyatta_op_last_comp=${_vyatta_op_last_comp_init} + else + COMPREPLY=( "${_vyatta_pipe_completions[@]}" ) + _vyatta_op_last_comp="${COMP_WORDS[*]}" + if [ ${#COMPREPLY[@]} -eq 1 ]; then + COMPREPLY=( "${COMPREPLY[0]} " ) + fi + fi + eval "$restore_shopts" + return + fi + + # this needs to be done on every completion even if it is the 'same' comp. + # The cursor can be at different places in the string. + # this will lead to unexpected cases if setting the node path isn't attempted + # each time. + if ! _vyatta_op_set_node_path ; then + echo -ne \\a + _vyatta_op_invalid_completion + COMPREPLY=( "" " " ) + eval "$restore_shopts" + return 1 + fi + + if [ "${COMP_WORDS[*]:0:$[$COMP_CWORD+1]}" != "$_vyatta_op_last_comp" ] ; then + _vyatta_set_comptype + case $_vyatta_comptype in + 'imagefiles') + _has_comptype=1 + _vyatta_image_file_complete + ;; + *) + _has_comptype=0 + if [[ -z "$current_word" ]]; then + _vyatta_op_set_completions $cur + else + _vyatta_op_set_completions $current_prefix + fi + ;; + esac + fi + if [[ $_has_comptype == 1 ]]; then + COMPREPLY=( "${_vyatta_op_completions[@]}" ) + else + COMPREPLY=($( compgen -W "${_vyatta_op_completions[*]}" -- $current_prefix )) + fi + + # if the last command line arg is empty and we have + # an empty completion option (meaning wild card), + # append a blank(s) to the completion array to force ambiguity + if [ -z "$current_prefix" -a -n "$current_word" ] || + [[ "${COMPREPLY[0]}" =~ "$cur" ]]; then + for comp ; do + if [ -z "$comp" ] ; then + if [ ${#COMPREPLY[@]} -eq 0 ] ; then + COMPREPLY=( " " "" ) + elif _vyatta_op_comprely_needs_ambiguity ; then + COMPREPLY+=( " " ) + fi + fi + done + fi + # Set this environment to enable and disable debugging on the fly + if [[ $DBG_OP_COMPS -eq 1 ]]; then + echo -e "\nCurrent: '$cur'" + echo -e "Current word: '$current_word'" + echo -e "Current prefix: '$current_prefix'" + echo "Number of comps: ${#_vyatta_op_completions[*]}" + echo "Number of non-comps: ${#_vyatta_op_noncompletions[*]}" + echo "_vyatta_op_completions: '${_vyatta_op_completions[*]}'" + echo "COMPREPLY: '${COMPREPLY[@]}'" + echo "CWORD: $COMP_CWORD" + echo "Last comp: '$_vyatta_op_last_comp'" + echo -e "Current comp: '${COMP_WORDS[*]:0:$[$COMP_CWORD+1]}'\n" + fi + + # This is non obvious... + # To have completion continue to work when working with words that aren't the last word, + # we have to set nospace at the beginning of this script and then append the spaces here. + if [ ${#COMPREPLY[@]} -eq 1 ] && + [[ $_has_comptype -ne 1 ]]; then + COMPREPLY=( "${COMPREPLY[0]} " ) + fi + # if there are no completions then handle invalid commands + if [ ${#_vyatta_op_noncompletions[@]} -eq 0 ] && + [ ${#_vyatta_op_completions[@]} -eq 0 ]; then + _vyatta_op_invalid_completion + COMPREPLY=( "" " " ) + _vyatta_op_last_comp=${_vyatta_op_last_comp_init} + elif [ ${#COMPREPLY[@]} -eq 0 ] && + [ -n "$current_prefix" ]; then + _vyatta_op_invalid_completion + COMPREPLY=( "" " " ) + _vyatta_op_last_comp=${_vyatta_op_last_comp_init} + # Stop completions from getting stuck + elif [ ${#_vyatta_op_completions[@]} -eq 1 ] && + [ -n "$cur" ] && + [[ "${COMPREPLY[0]}" =~ "$cur" ]]; then + _vyatta_op_last_comp=${_vyatta_op_last_comp_init} + elif [ ${#_vyatta_op_completions[@]} -eq 1 ] && + [ -n "$current_prefix" ] && + [[ "${COMPREPLY[0]}" =~ "$current_prefix" ]]; then + _vyatta_op_last_comp=${_vyatta_op_last_comp_init} + # if there are no completions then always show the non-comps + elif [ "${COMP_WORDS[*]:0:$[$COMP_CWORD+1]}" == "$_vyatta_op_last_comp" ] || + [ ${#_vyatta_op_completions[@]} -eq 0 ] || + [ -z "$cur" ]; then + _vyatta_op_help "$current_prefix" \ + "${_vyatta_op_noncompletions[@]}" \ + "${_vyatta_op_completions[@]}" \ + | ${VYATTA_PAGER:-cat} + COMPREPLY=( "" " " ) + _vyatta_op_last_comp=${_vyatta_op_last_comp_init} + else + _vyatta_op_last_comp="${COMP_WORDS[*]:0:$[$COMP_CWORD+1]}" + fi + + eval "$restore_shopts" +} + +# "pipe" functions +count () +{ + wc -l +} + +match () +{ + grep -E -e "$1" +} + +no-match () +{ + grep -E -v -e "$1" +} + +no-more () +{ + cat +} + +strip-private () +{ + ${vyos_libexec_dir}/strip-private.py +} + +commands () +{ + if [ "$_OFR_CONFIGURE" != "" ]; then + if $(cli-shell-api sessionChanged); then + echo "You have uncommited changes, please commit them before using the commands pipe" + else + vyos-config-to-commands + fi + else + echo "commands pipe is not supported in operational mode" + fi +} + +json () +{ + if [ "$_OFR_CONFIGURE" != "" ]; then + if $(cli-shell-api sessionChanged); then + echo "You have uncommited changes, please commit them before using the JSON pipe" + else + vyos-config-to-json + fi + else + echo "JSON pipe is not supported in operational mode" + fi +} + +# pipe command help +# $1: command +_vyatta_pipe_help () +{ + local help="No help text available" + case "$1" in + count) help="Count the number of lines in the output";; + match) help="Only output lines that match specified pattern";; + no-match) help="Only output lines that do not match specified pattern";; + more) help="Paginate the output";; + no-more) help="Do not paginate the output";; + strip-private) help="Remove private information from the config";; + commands) help="Convert config to set commands";; + json) help="Convert config to JSON";; + '<pattern>') help="Pattern for matching";; + esac + echo -n "$help" +} + +_vyatta_do_pipe_help () +{ + local help='' + if (( ${#_vyatta_pipe_completions[@]} + ${#_vyatta_pipe_noncompletions[@]} + == 0 )); then + return + fi + echo -en "\nPossible completions:" + for comp in "${_vyatta_pipe_completions[@]}" \ + "${_vyatta_pipe_noncompletions[@]}"; do + _vyatta_op_print_help "$comp" "$(_vyatta_pipe_help "$comp")" + done +} + +# pipe completion +# $@: words +_vyatta_pipe_completion () +{ + local -a pipe_cmd=() + local -a all_cmds=( 'count' 'match' 'no-match' 'more' 'no-more' 'strip-private' 'commands' 'json' ) + local found=0 + _vyatta_pipe_completions=() + _vyatta_pipe_noncompletions=() + + for word in "$@"; do + if [[ "$found" == "1" || "$word" == "|" ]]; then + pipe_cmd+=( "$word" ) + found=1 + fi + done + if (( found == 0 )); then + return 1 + fi + if (( ${#pipe_cmd[@]} == 1 )); then + # "|" only + _vyatta_pipe_completions=( "${all_cmds[@]}" ) + return 0 + fi + if (( ${#pipe_cmd[@]} == 2 )); then + # "|<space, chars, or space+chars>" + _vyatta_pipe_completions=($(compgen -W "${all_cmds[*]}" -- ${pipe_cmd[1]})) + return 0 + fi + if (( ${#pipe_cmd[@]} == 3 )); then + # "|<chars or space+chars><space or space+chars>" + case "${pipe_cmd[1]}" in + match|no-match) _vyatta_pipe_noncompletions=( '<pattern>' );; + esac + return 0 + fi + return 0 +} + +# comptype +_vyatta_set_comptype () +{ + local comptype + unset _vyatta_comptype + for ndef in ${_vyatta_op_node_path}/*/node.def ; do + if [[ $ndef == */node.tag/node.def ]] ; then + local comptype=$( _vyatta_op_get_node_def_field $ndef comptype ) + if [[ $comptype == "imagefiles" ]] ; then + _vyatta_comptype=$comptype + return 0 + else + _vyatta_comptype="" + return 1 + fi + else + _vyatta_comptype="" + return 1 + fi + done +} + +_filedir_xspec_vyos() +{ + local cur prev words cword + _init_completion || return + + _tilde "$cur" || return 0 + + local IFS=$'\n' xspec=${_xspec[${1##*/}]} tmp + local -a toks + + toks=( $( + compgen -d -- "$(quote_readline "$cur")" | { + while read -r tmp; do + printf '%s\n' $tmp + done + } + )) + + # Munge xspec to contain uppercase version too + # http://thread.gmane.org/gmane.comp.shells.bash.bugs/15294/focus=15306 + eval xspec="${xspec}" + local matchop=! + if [[ $xspec == !* ]]; then + xspec=${xspec#!} + matchop=@ + fi + xspec="$matchop($xspec|${xspec^^})" + + toks+=( $( + eval compgen -f -X "!$xspec" -- "\$(quote_readline "\$cur")" | { + while read -r tmp; do + [[ -n $tmp ]] && printf '%s\n' $tmp + done + } + )) + + if [[ ${#toks[@]} -ne 0 ]]; then + compopt -o filenames + COMPREPLY=( "${toks[@]}" ) + fi +} + +nullglob_save=$( shopt -p nullglob ) +shopt -s nullglob +for f in ${vyatta_datadir}/vyatta-op/functions/allowed/* ; do + source $f +done +eval $nullglob_save +unset nullglob_save + +# don't initialize if we are in configure mode +if [ "$_OFR_CONFIGURE" == "ok" ]; then + return 0 +fi + +if [[ "$VYATTA_USER_LEVEL_DIR" != "/opt/vyatta/etc/shell/level/admin" ]]; then + vyatta_unpriv_init $@ +else + _vyatta_op_init $@ +fi + +### Local Variables: +### mode: shell-script +### End: diff --git a/src/etc/commit/post-hooks.d/00vyos-sync b/src/etc/commit/post-hooks.d/00vyos-sync new file mode 100644 index 0000000..8ec732d --- /dev/null +++ b/src/etc/commit/post-hooks.d/00vyos-sync @@ -0,0 +1,7 @@ +#!/bin/sh +# When power is lost right after a commit modified files, the +# system can be corrupted and e.g. login is no longer possible. +# Always sync files to the backend storage after a commit. +# https://vyos.dev/T4975 +sync + diff --git a/src/etc/cron.d/vyos-geoip b/src/etc/cron.d/vyos-geoip new file mode 100644 index 0000000..9bb38a8 --- /dev/null +++ b/src/etc/cron.d/vyos-geoip @@ -0,0 +1 @@ +30 4 * * 1 root sg vyattacfg "/usr/libexec/vyos/geoip-update.py --force" >/tmp/geoip-update.log 2>&1 diff --git a/src/etc/default/vyatta b/src/etc/default/vyatta new file mode 100644 index 0000000..e5fa3bb --- /dev/null +++ b/src/etc/default/vyatta @@ -0,0 +1,217 @@ +# **** License **** +# 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 +# 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. +# +# This code was originally developed by Vyatta, Inc. +# Portions created by Vyatta are Copyright (C) 2006, 2007 Vyatta, Inc. +# All Rights Reserved. + +# declare configured Vyatta shell environment variables + +# first set vars per args of the "source /etc/default/vyatta VAR=FOO" +_vyatta_extglob=$(shopt -p extglob) +shopt -s extglob +for arg ; do + [[ $arg == *=* ]] && \ + eval declare -x $arg +done +eval $_vyatta_extglob +unset _vyatta_extglob + +{ + # These declarations must go within braces in order to be able to silence + # readonly variable errors. + + for var in prefix exec_prefix datarootdir ; do + eval test -n \"\$$var\" \&\& _vyatta_save_$var=\$$var + done + + prefix=/opt/vyatta + exec_prefix=${prefix} + datarootdir=${prefix}/share + + if test -z "$vyatta_prefix" ; then + if test -n "/opt/vyatta" ; then + declare -x -r vyatta_prefix=/opt/vyatta + declare -x -r vyos_prefix=/opt/vyatta + else + declare -x -r vyatta_prefix=/opt/vyatta + declare -x -r vyos_prefix=/opt/vyatta + fi + fi + if test -z "$vyatta_exec_prefix" ; then + if test -n "${prefix}" ; then + declare -x -r vyatta_prefix=${prefix} + declare -x -r vyos_prefix=${prefix} + else + declare -x -r vyatta_prefix=$vyatta_prefix + declare -x -r vyos_prefix=$vyatta_prefix + fi + fi + if test -z "$vyatta_datarootdir" ; then + if test -n "${prefix}/share" ; then + declare -x -r vyatta_datarootdir=${prefix}/share + declare -x -r vyos_datarootdir=${prefix}/share + else + declare -x -r vyatta_datarootdir=$vyatta_prefix/share + declare -x -r vyos_datarootdir=$vyatta_prefix/share + fi + fi + if test -z "$vyatta_bindir" ; then + if test -n "${exec_prefix}/bin" ; then + declare -x -r vyatta_bindir=${exec_prefix}/bin + else + declare -x -r vyatta_bindir=$vyatta_exec_prefix/bin + fi + fi + if test -z "$vyatta_sbindir" ; then + if test -n "${exec_prefix}/sbin" ; then + declare -x -r vyatta_sbindir=${exec_prefix}/sbin + else + declare -x -r vyatta_sbindir=$vyatta_exec_prefix/sbin + fi + fi + if test -z "$vyatta_libdir" ; then + if test -n "${exec_prefix}/lib" ; then + declare -x -r vyatta_libdir=${exec_prefix}/lib + declare -x -r vyos_libdir=${exec_prefix}/lib + else + declare -x -r vyatta_libdir=$vyatta_exec_prefix/lib + declare -x -r vyos_libdir=$vyatta_exec_prefix/lib + fi + fi + if test -z "$vyatta_libexecdir" ; then + if test -n "${exec_prefix}/libexec" ; then + declare -x -r vyatta_libexecdir=${exec_prefix}/libexec + else + declare -x -r vyatta_libexecdir=$vyatta_exec_prefix/libexec + fi + fi + if test -z "$vyatta_datadir" ; then + if test -n "${datarootdir}" ; then + declare -x -r vyatta_datadir=${datarootdir} + declare -x -r vyos_datadir=${datarootdir} + else + declare -x -r vyatta_datadir=$vyatta_datarootdir + declare -x -r vyos_datadir=$vyatta_datarootdir + fi + fi + if test -z "$vyatta_htmldir" ; then + if test -n "${docdir}" ; then + declare -x -r vyatta_htmldir=${docdir} + else + declare -x -r vyatta_htmldir=$vyatta_datarootdir/html + fi + fi + if test -z "$vyatta_infodir" ; then + if test -n "${prefix}/share/info" ; then + declare -x -r vyatta_infodir=${prefix}/share/info + else + declare -x -r vyatta_infodir=$vyatta_datarootdir/info + fi + fi + if test -z "$vyatta_mandir" ; then + if test -n "${prefix}/share/man" ; then + declare -x -r vyatta_htmldir=${prefix}/share/man + else + declare -x -r vyatta_htmldir=$vyatta_datarootdir/man + fi + fi + if test -z "$vyatta_localedir" ; then + if test -n "${datarootdir}/locale" ; then + declare -x -r vyatta_localedir=${datarootdir}/locale + else + declare -x -r vyatta_localedir=$vyatta_datarootdir/locale + fi + fi + if test -z "$vyatta_localstatedir" ; then + if test -n "${prefix}/var" ; then + declare -x -r vyatta_localstatedir=${prefix}/var + else + declare -x -r vyatta_localstatedir=$vyatta_prefix/var + fi + fi + if test -z "$vyatta_sharedstatedir" ; then + if test -n "${prefix}/com" ; then + declare -x -r vyatta_sharedstatedir=${prefix}/com + else + declare -x -r vyatta_sharedstatedir=$vyatta_prefix/com + fi + fi + if test -z "$vyatta_sysconfdir" ; then + if test -n "${prefix}/etc" ; then + declare -x -r vyatta_sysconfdir=${prefix}/etc + else + declare -x -r vyatta_sysconfdir=$vyatta_prefix/etc + fi + fi + if test -z "$vyatta_op_templates" ; then + declare -x -r vyatta_op_templates=$vyatta_datadir/vyatta-op/templates + declare -x -r vyos_op_templates=$vyatta_datadir/vyatta-op/templates + fi + if test -z "$vyatta_cfg_templates" ; then + declare -x -r vyatta_cfg_templates=$vyatta_datadir/vyatta-cfg/templates + declare -x -r vyos_cfg_templates=$vyatta_datadir/vyatta-cfg/templates + fi + if test -z "$vyatta_configdir" ; then + declare -x -r vyatta_configdir=$vyatta_prefix/config + declare -x -r vyos_configdir=$vyatta_prefix/config + fi + + for var in prefix exec_prefix datarootdir ; do + eval test -n \"\$_vyatta_save_$var\" \&\& $var=\$_vyatta_save_$var + done + + # It's not like we do, or should support installing VyOS at a different prefix + declare -x -r vyos_libexec_dir=/usr/libexec/vyos + declare -x -r vyos_bin_dir=/usr/bin + declare -x -r vyos_sbin_dir=/usr/sbin + declare -x -r vyos_share_dir=/usr/share + + if test -z "$vyos_conf_scripts_dir" ; then + declare -x -r vyos_conf_scripts_dir=$vyos_libexec_dir/conf_mode + fi + if test -z "$vyos_op_scripts_dir" ; then + declare -x -r vyos_op_scripts_dir=$vyos_libexec_dir/op_mode + fi + if test -z "$vyos_completion_dir" ; then + declare -x -r vyos_completion_dir=$vyos_libexec_dir/completion + fi + if test -z "$vyos_validators_dir" ; then + declare -x -r vyos_validators_dir=$vyos_libexec_dir/validators + fi + if test -z "$vyos_data_dir" ; then + declare -x -r vyos_data_dir=$vyos_share_dir/vyos + fi + if test -z "$vyos_persistence_dir" ; then + UNION_NAME=$(cat /proc/cmdline | sed -e s+^.*vyos-union=++ | sed -e 's/ .*$//') + declare -x -r vyos_persistence_dir="/usr/lib/live/mount/persistence/${UNION_NAME}" + fi + if test -z "$vyos_rootfs_dir" ; then + ROOTFS=$(mount -t squashfs | grep loop0 | cut -d' ' -f3) + declare -x -r vyos_rootfs_dir="${ROOTFS}" + fi + if test -z "$VRF" ; then + VRF=$(ip vrf identify) + [ -n "$VRF" ] && declare -x -r VRF="${VRF}" + fi + if test -z "$NETNS" ; then + NETNS=$(ip netns identify) + [ -n "$NETNS" ] && declare -x -r NETNS="${NETNS}" + fi + +} 2>/dev/null || : + +[ -r /etc/default/vyatta-cfg ] && source /etc/default/vyatta-cfg + +[ -r /etc/default/vyatta-local-env ] && source /etc/default/vyatta-local-env + +### Local Variables: +### mode: shell-script +### End: diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/01-vyos-logging b/src/etc/dhcp/dhclient-enter-hooks.d/01-vyos-logging new file mode 100644 index 0000000..121fb21 --- /dev/null +++ b/src/etc/dhcp/dhclient-enter-hooks.d/01-vyos-logging @@ -0,0 +1,20 @@ +# enable logging +LOG_ENABLE="yes" +LOG_STDERR="no" +LOG_TAG="dhclient-script-vyos" + +function logmsg () { + # log message to journal + case $1 in + error) LOG_PRIO="daemon.err" ;; + info) LOG_PRIO="daemon.info" ;; + esac + + if [ "${LOG_ENABLE}" == "yes" ] ; then + if [ "${LOG_STDERR}" == "yes" ] ; then + /usr/bin/logger -e --id=$$ -s -p ${LOG_PRIO} -t ${LOG_TAG} "${@:2}" + else + /usr/bin/logger -e --id=$$ -p ${LOG_PRIO} -t ${LOG_TAG} "${@:2}" + fi + fi +} diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/02-vyos-stopdhclient b/src/etc/dhcp/dhclient-enter-hooks.d/02-vyos-stopdhclient new file mode 100644 index 0000000..ae6bf9f --- /dev/null +++ b/src/etc/dhcp/dhclient-enter-hooks.d/02-vyos-stopdhclient @@ -0,0 +1,38 @@ +# skip all of this if dhclient-script running by stop command defined below +if [ -z ${CONTROLLED_STOP} ] ; then + # stop dhclient for this interface, if it is not current one + # get PID for current dhclient + current_dhclient=`ps --no-headers --format ppid --pid $$ | awk '{ print \$1 }'` + + # get PID for master process (current can be a fork) + master_dhclient=`ps --no-headers --format ppid --pid $current_dhclient | awk '{ print \$1 }'` + + # get IP version for current dhclient + ipversion_arg=`ps --no-headers --format args --pid $current_dhclient | awk 'match(\$0, /\s-(4|6)\s/, IPV) { printf("%s", IPV[1]) }'` + + # get list of all dhclient running for current interface + if [[ $ipversion_arg == "6" ]]; then + dhclients_pids=(`pgrep -f "dhclient.*\s-6\s.*\s$interface(\s|$)"`) + else + dhclients_pids=(`ps --no-headers --format pid,args -C dhclient | awk "{ if(match(\\$0, /\s${interface}(\s|$)/) && !match(\\$0, /\s-6\s/)) printf(\"%s\n\", \\$1) }"`) + fi + + logmsg info "Current dhclient PID: $current_dhclient, Parent PID: $master_dhclient, IP version: $ipversion_arg, All dhclients for interface $interface: ${dhclients_pids[@]}" + # stop all dhclients for current interface, except current one + for dhclient in ${dhclients_pids[@]}; do + if ([ $dhclient -ne $current_dhclient ] && [ $dhclient -ne $master_dhclient ]); then + # get path to PID-file of dhclient process + local dhclient_pidfile=`ps --no-headers --format args --pid $dhclient | awk 'match(\$0, ".*-pf (/.*pid) .*", PF) { print PF[1] }'` + # get path to lease-file of dhclient process + local dhclient_leasefile=`ps --no-headers --format args --pid $dhclient | awk 'match(\$0, ".*-lf (/\\\S*leases) .*", LF) { print LF[1] }'` + # stop dhclient with native command - this will run dhclient-script with correct reason unlike simple kill + logmsg info "Stopping dhclient with PID: ${dhclient}, PID file: ${dhclient_pidfile}, Leases file: ${dhclient_leasefile}" + if [[ -e $dhclient_pidfile ]]; then + dhclient -e CONTROLLED_STOP=yes -x -pf $dhclient_pidfile -lf $dhclient_leasefile + else + logmsg error "PID file $dhclient_pidfile does not exists, killing dhclient with SIGTERM signal" + kill -s 15 ${dhclient} + fi + fi + done +fi diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper b/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper new file mode 100644 index 0000000..2a1c5a7 --- /dev/null +++ b/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper @@ -0,0 +1,110 @@ +# redefine ip command to use FRR when it is available + +# default route distance +IF_METRIC=${IF_METRIC:-210} + +# Check if interface is inside a VRF +VRF_OPTION=$(/usr/sbin/ip -j -d link show ${interface} | awk '{if(match($0, /.*"master":"(\w+)".*"info_slave_kind":"vrf"/, IFACE_DETAILS)) printf("vrf %s", IFACE_DETAILS[1])}') + +# get status of FRR +function frr_alive () { + /usr/lib/frr/watchfrr.sh all_status + if [ "$?" -eq "0" ] ; then + logmsg info "FRR status: running" + return 0 + else + logmsg info "FRR status: not running" + return 1 + fi +} + +# convert ip route command to vtysh +function iptovtysh () { + # prepare variables for vtysh command + local VTYSH_ACTION=$3 + local VTYSH_NETADDR="" + local VTYSH_GATEWAY="" + local VTYSH_DEV="" + local VTYSH_TAG="210" + local VTYSH_DISTANCE=$IF_METRIC + # convert default route to 0.0.0.0/0 + if [ "$4" == "default" ] ; then + VTYSH_NETADDR="0.0.0.0/0" + else + VTYSH_NETADDR=$4 + fi + # add /32 to ip addresses without netmasks + if [[ ! $VTYSH_NETADDR =~ ^.*/[[:digit:]]+$ ]] ; then + VTYSH_NETADDR="$VTYSH_NETADDR/32" + fi + shift 4 + # get gateway address + if [ "$1" == "via" ] ; then + VTYSH_GATEWAY=$2 + shift 2 + fi + # get device name + if [ "$1" == "dev" ]; then + VTYSH_DEV=$2 + shift 2 + fi + # get distance + if [ "$1" == "metric" ]; then + VTYSH_DISTANCE=$2 + shift 2 + fi + + VTYSH_CMD="ip route $VTYSH_NETADDR $VTYSH_GATEWAY $VTYSH_DEV tag $VTYSH_TAG $VTYSH_DISTANCE $VRF_OPTION" + + # delete route if the command is "del" + if [ "$VTYSH_ACTION" == "del" ] ; then + VTYSH_CMD="no $VTYSH_CMD" + fi + logmsg info "Converted vtysh command: \"$VTYSH_CMD\"" +} + +# delete the same route from kernel before adding new one +function delroute () { + logmsg info "Checking if the route presented in kernel: $@ $VRF_OPTION" + if /usr/sbin/ip route show $@ $VRF_OPTION | grep -qx "$1 " ; then + logmsg info "Deleting IP route: \"/usr/sbin/ip route del $@ $VRF_OPTION\"" + /usr/sbin/ip route del $@ $VRF_OPTION + fi +} + +# try to communicate with vtysh +function vtysh_conf () { + # perform 10 attempts with 1 second delay for retries + for i in {1..10} ; do + if vtysh -c "conf t" -c "$1" ; then + logmsg info "Command was executed successfully via vtysh: \"$1\"" + return 0 + else + logmsg info "Failed to send command to vtysh, retrying in 1 second" + sleep 1 + fi + done + logmsg error "Failed to execute command via vtysh after 10 attempts: \"$1\"" + return 1 +} + +# replace ip command with this wrapper +function ip () { + # pass comand to system `ip` if this is not related to routes change + if [ "$2" != "route" ] ; then + logmsg info "Passing command to /usr/sbin/ip: \"$@\"" + /usr/sbin/ip $@ + else + # if we want to work with routes, try to use FRR first + if frr_alive ; then + delroute ${@:4} + iptovtysh $@ + logmsg info "Sending command to vtysh" + vtysh_conf "$VTYSH_CMD" + else + # add ip route to kernel + logmsg info "Modifying routes in kernel: \"/usr/sbin/ip $@\"" + /usr/sbin/ip $@ $VRF_OPTION + fi + fi +} diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf b/src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf new file mode 100644 index 0000000..9a8a53b --- /dev/null +++ b/src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf @@ -0,0 +1,32 @@ +# modified make_resolv_conf() for VyOS +# should be used only if vyos-hostsd is running + +if /usr/bin/systemctl -q is-active vyos-hostsd; then + make_resolv_conf() { + hostsd_client="/usr/bin/vyos-hostsd-client" + hostsd_changes= + + if [ -n "$new_domain_name" ]; then + logmsg info "Deleting search domains with tag \"dhcp-$interface\" via vyos-hostsd-client" + $hostsd_client --delete-search-domains --tag "dhcp-$interface" + logmsg info "Adding domain name \"$new_domain_name\" as search domain with tag \"dhcp-$interface\" via vyos-hostsd-client" + $hostsd_client --add-search-domains "$new_domain_name" --tag "dhcp-$interface" + hostsd_changes=y + fi + + if [ -n "$new_domain_name_servers" ]; then + logmsg info "Deleting nameservers with tag \"dhcp-$interface\" via vyos-hostsd-client" + $hostsd_client --delete-name-servers --tag "dhcp-$interface" + logmsg info "Adding nameservers \"$new_domain_name_servers\" with tag \"dhcp-$interface\" via vyos-hostsd-client" + $hostsd_client --add-name-servers $new_domain_name_servers --tag "dhcp-$interface" + hostsd_changes=y + fi + + if [ $hostsd_changes ]; then + logmsg info "Applying changes via vyos-hostsd-client" + $hostsd_client --apply + else + logmsg info "No changes to apply via vyos-hostsd-client" + fi + } +fi diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/05-vyos-mtureplace b/src/etc/dhcp/dhclient-enter-hooks.d/05-vyos-mtureplace new file mode 100644 index 0000000..4a08765 --- /dev/null +++ b/src/etc/dhcp/dhclient-enter-hooks.d/05-vyos-mtureplace @@ -0,0 +1,38 @@ +# replace MTU with value from configuration + +# get MTU value via Python +# as configuration is not available to cli-shell-api at the first boot, we must use vyos.config, which contain workaround for this, instead clean shell +function get_mtu { +python3 - <<PYEND +from vyos.config import Config +import os +import re + +# check if interface is not VLAN and fix name if necessary +interface_name = os.getenv('interface', '') +regex_filter = re.compile('^(?P<interface>\w+)\.(?P<vid>\d+)$') +if regex_filter.search(interface_name): + iface = regex_filter.search(interface_name).group('interface') + vid = regex_filter.search(interface_name).group('vid') + interface_name = "{} vif {}".format(iface, vid) + +# initialize config +config = Config() +if config.exists('interfaces'): + iface_types = config.list_nodes('interfaces') + for iface_type in iface_types: + # check if configuration contain MTU value for interface and return (print) it + if config.exists("interfaces {} {} mtu".format(iface_type, interface_name)): + print(format(config.return_value("interfaces {} {} mtu".format(iface_type, interface_name)))) +PYEND +} + +# check if DHCP server return MTU value +if [ -n "$new_interface_mtu" ]; then + # try to get MTU from config and replace original one + configured_mtu="$(get_mtu)" + if [[ -n $configured_mtu ]] ; then + logmsg info "Replacing MTU value for $interface with preconfigured one: $configured_mtu" + new_interface_mtu="$configured_mtu" + fi +fi diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/99-run-user-hooks b/src/etc/dhcp/dhclient-enter-hooks.d/99-run-user-hooks new file mode 100644 index 0000000..570758b --- /dev/null +++ b/src/etc/dhcp/dhclient-enter-hooks.d/99-run-user-hooks @@ -0,0 +1,5 @@ +#!/bin/bash +DHCP_PRE_HOOKS="/config/scripts/dhcp-client/pre-hooks.d/" +if [ -d "${DHCP_PRE_HOOKS}" ] ; then + run_hookdir "${DHCP_PRE_HOOKS}" +fi diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup b/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup new file mode 100644 index 0000000..da1bda1 --- /dev/null +++ b/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup @@ -0,0 +1,115 @@ +## +## VyOS cleanup +## +# NOTE: here we use 'ip' wrapper, therefore a route will be actually deleted via /usr/sbin/ip or vtysh, according to the system state +hostsd_client="/usr/bin/vyos-hostsd-client" +hostsd_changes= +# check vyos-hostsd status +/usr/bin/systemctl -q is-active vyos-hostsd +hostsd_status=$? + +if [[ $reason =~ ^(EXPIRE|FAIL|RELEASE|STOP)$ ]]; then + if [[ $hostsd_status -eq 0 ]]; then + # delete search domains and nameservers via vyos-hostsd + logmsg info "Deleting search domains with tag \"dhcp-$interface\" via vyos-hostsd-client" + $hostsd_client --delete-search-domains --tag "dhcp-$interface" + logmsg info "Deleting nameservers with tag \"dhcp-${interface}\" via vyos-hostsd-client" + $hostsd_client --delete-name-servers --tag "dhcp-${interface}" + hostsd_changes=y + fi + + if_metric="$IF_METRIC" + + # try to delete default ip route + for router in $old_routers; do + # check if we are bound to a VRF + local vrf_name=$(basename /sys/class/net/${interface}/upper_* | sed -e 's/upper_//') + if [ "$vrf_name" != "*" ]; then + vrf="vrf $vrf_name" + fi + + logmsg info "Deleting default route: via $router dev ${interface} ${if_metric:+metric $if_metric} ${vrf}" + ip -4 route del default via $router dev ${interface} ${if_metric:+metric $if_metric} ${vrf} + + if_metric=$((if_metric+1)) + done + + # delete rfc3442 routes + if [ -n "$old_rfc3442_classless_static_routes" ]; then + set -- $old_rfc3442_classless_static_routes + while [ $# -gt 0 ]; do + net_length=$1 + via_arg='' + case $net_length in + 32|31|30|29|28|27|26|25) + if [ $# -lt 9 ]; then + return 1 + fi + net_address="${2}.${3}.${4}.${5}" + gateway="${6}.${7}.${8}.${9}" + shift 9 + ;; + 24|23|22|21|20|19|18|17) + if [ $# -lt 8 ]; then + return 1 + fi + net_address="${2}.${3}.${4}.0" + gateway="${5}.${6}.${7}.${8}" + shift 8 + ;; + 16|15|14|13|12|11|10|9) + if [ $# -lt 7 ]; then + return 1 + fi + net_address="${2}.${3}.0.0" + gateway="${4}.${5}.${6}.${7}" + shift 7 + ;; + 8|7|6|5|4|3|2|1) + if [ $# -lt 6 ]; then + return 1 + fi + net_address="${2}.0.0.0" + gateway="${3}.${4}.${5}.${6}" + shift 6 + ;; + 0) # default route + if [ $# -lt 5 ]; then + return 1 + fi + net_address="0.0.0.0" + gateway="${2}.${3}.${4}.${5}" + shift 5 + ;; + *) # error + return 1 + ;; + esac + # take care of link-local routes + if [ "${gateway}" != '0.0.0.0' ]; then + via_arg="via ${gateway}" + fi + # delete route (ip detects host routes automatically) + ip -4 route del "${net_address}/${net_length}" \ + ${via_arg} dev "${interface}" >/dev/null 2>&1 + done + fi +fi + +if [[ $reason =~ ^(EXPIRE6|RELEASE6|STOP6)$ ]]; then + if [[ $hostsd_status -eq 0 ]]; then + # delete search domains and nameservers via vyos-hostsd + logmsg info "Deleting search domains with tag \"dhcpv6-$interface\" via vyos-hostsd-client" + $hostsd_client --delete-search-domains --tag "dhcpv6-$interface" + logmsg info "Deleting nameservers with tag \"dhcpv6-${interface}\" via vyos-hostsd-client" + $hostsd_client --delete-name-servers --tag "dhcpv6-${interface}" + hostsd_changes=y + fi +fi + +if [ $hostsd_changes ]; then + logmsg info "Applying changes via vyos-hostsd-client" + $hostsd_client --apply +else + logmsg info "No changes to apply via vyos-hostsd-client" +fi diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/02-vyos-dhcp-renew-rfc3442 b/src/etc/dhcp/dhclient-exit-hooks.d/02-vyos-dhcp-renew-rfc3442 new file mode 100644 index 0000000..9202fe7 --- /dev/null +++ b/src/etc/dhcp/dhclient-exit-hooks.d/02-vyos-dhcp-renew-rfc3442 @@ -0,0 +1,148 @@ +# support for RFC3442 routes in DHCP RENEW + +function convert_to_cidr () { + cidr="" + set -- $1 + while [ $# -gt 0 ]; do + net_length=$1 + + case $net_length in + 32|31|30|29|28|27|26|25) + if [ $# -lt 9 ]; then + return 1 + fi + net_address="${2}.${3}.${4}.${5}" + gateway="${6}.${7}.${8}.${9}" + shift 9 + ;; + 24|23|22|21|20|19|18|17) + if [ $# -lt 8 ]; then + return 1 + fi + net_address="${2}.${3}.${4}.0" + gateway="${5}.${6}.${7}.${8}" + shift 8 + ;; + 16|15|14|13|12|11|10|9) + if [ $# -lt 7 ]; then + return 1 + fi + net_address="${2}.${3}.0.0" + gateway="${4}.${5}.${6}.${7}" + shift 7 + ;; + 8|7|6|5|4|3|2|1) + if [ $# -lt 6 ]; then + return 1 + fi + net_address="${2}.0.0.0" + gateway="${3}.${4}.${5}.${6}" + shift 6 + ;; + 0) # default route + if [ $# -lt 5 ]; then + return 1 + fi + net_address="0.0.0.0" + gateway="${2}.${3}.${4}.${5}" + shift 5 + ;; + *) # error + return 1 + ;; + esac + + cidr+="${net_address}/${net_length}:${gateway} " + done +} + +# main script starts here + +RUN="yes" + +if [ "$RUN" = "yes" ]; then + convert_to_cidr "$old_rfc3442_classless_static_routes" + old_cidr=$cidr + convert_to_cidr "$new_rfc3442_classless_static_routes" + new_cidr=$cidr + + if [ "$reason" = "RENEW" ]; then + if [ "$new_rfc3442_classless_static_routes" != "$old_rfc3442_classless_static_routes" ]; then + logmsg info "RFC3442 route change detected, old_routes: $old_rfc3442_classless_static_routes" + logmsg info "RFC3442 route change detected, new_routes: $new_rfc3442_classless_static_routes" + if [ -z "$new_rfc3442_classless_static_routes" ]; then + # delete all routes from the old_rfc3442_classless_static_routes + for route in $old_cidr; do + network=$(printf "${route}" | awk -F ":" '{print $1}') + gateway=$(printf "${route}" | awk -F ":" '{print $2}') + # take care of link-local routes + if [ "${gateway}" != '0.0.0.0' ]; then + via_arg="via ${gateway}" + else + via_arg="" + fi + ip -4 route del "${network}" "${via_arg}" dev "${interface}" >/dev/null 2>&1 + done + elif [ -z "$old_rfc3442_classless_static_routes" ]; then + # add all routes from the new_rfc3442_classless_static_routes + for route in $new_cidr; do + network=$(printf "${route}" | awk -F ":" '{print $1}') + gateway=$(printf "${route}" | awk -F ":" '{print $2}') + # take care of link-local routes + if [ "${gateway}" != '0.0.0.0' ]; then + via_arg="via ${gateway}" + else + via_arg="" + fi + ip -4 route add "${network}" "${via_arg}" dev "${interface}" >/dev/null 2>&1 + done + else + # update routes + # delete old + for old_route in $old_cidr; do + match="false" + for new_route in $new_cidr; do + if [[ "$old_route" == "$new_route" ]]; then + match="true" + break + fi + done + if [[ "$match" == "false" ]]; then + # delete old_route + network=$(printf "${old_route}" | awk -F ":" '{print $1}') + gateway=$(printf "${old_route}" | awk -F ":" '{print $2}') + # take care of link-local routes + if [ "${gateway}" != '0.0.0.0' ]; then + via_arg="via ${gateway}" + else + via_arg="" + fi + ip -4 route del "${network}" "${via_arg}" dev "${interface}" >/dev/null 2>&1 + fi + done + # add new + for new_route in $new_cidr; do + match="false" + for old_route in $old_cidr; do + if [[ "$new_route" == "$old_route" ]]; then + match="true" + break + fi + done + if [[ "$match" == "false" ]]; then + # add new_route + network=$(printf "${new_route}" | awk -F ":" '{print $1}') + gateway=$(printf "${new_route}" | awk -F ":" '{print $2}') + # take care of link-local routes + if [ "${gateway}" != '0.0.0.0' ]; then + via_arg="via ${gateway}" + else + via_arg="" + fi + ip -4 route add "${network}" "${via_arg}" dev "${interface}" >/dev/null 2>&1 + fi + done + fi + fi + fi +fi diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/03-vyos-dhclient-hook b/src/etc/dhcp/dhclient-exit-hooks.d/03-vyos-dhclient-hook new file mode 100644 index 0000000..d5e6462 --- /dev/null +++ b/src/etc/dhcp/dhclient-exit-hooks.d/03-vyos-dhclient-hook @@ -0,0 +1,46 @@ +#!/bin/sh + +# Author: Stig Thormodsrud <stig@vyatta.com> +# Date: 2007 +# Description: dhcp client hook + +# **** License **** +# 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 +# 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. +# +# This code was originally developed by Vyatta, Inc. +# Portions created by Vyatta are Copyright (C) 2006, 2007, 2008 Vyatta, Inc. +# All Rights Reserved. +# **** End License **** + +# To enable this script set the following variable to "yes" +RUN="yes" + +proto="" +if [[ $reason =~ ^(REBOOT6|INIT6|EXPIRE6|RELEASE6|STOP6|INFORM6|BOUND6|REBIND6|DELEGATED6)$ ]]; then + proto="v6" +fi + +if [ "$RUN" = "yes" ]; then + BASE_PATH=$(python3 -c "from vyos.defaults import directories; print(directories['isc_dhclient_dir'])") + mkdir -p ${BASE_PATH} + LOG=${BASE_PATH}/dhclient_"$interface"."$proto"lease + echo `date` > $LOG + + for i in reason interface new_expiry new_dhcp_lease_time medium \ + alias_ip_address new_ip_address new_broadcast_address \ + new_subnet_mask new_domain_name new_network_number \ + new_domain_name_servers new_routers new_static_routes \ + new_dhcp_server_identifier new_dhcp_message_type \ + old_ip_address old_subnet_mask old_domain_name \ + old_domain_name_servers old_routers \ + old_static_routes; do + echo $i=\'${!i}\' >> $LOG + done +fi diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/98-run-user-hooks b/src/etc/dhcp/dhclient-exit-hooks.d/98-run-user-hooks new file mode 100644 index 0000000..910b586 --- /dev/null +++ b/src/etc/dhcp/dhclient-exit-hooks.d/98-run-user-hooks @@ -0,0 +1,5 @@ +#!/bin/bash +DHCP_POST_HOOKS="/config/scripts/dhcp-client/post-hooks.d/" +if [ -d "${DHCP_POST_HOOKS}" ] ; then + run_hookdir "${DHCP_POST_HOOKS}" +fi diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook b/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook new file mode 100644 index 0000000..57f8030 --- /dev/null +++ b/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook @@ -0,0 +1,45 @@ +#!/bin/bash +# +# Copyright (C) 2021 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/>. + +DHCP_HOOK_IFLIST="/tmp/ipsec_dhcp_interfaces" + +if ! { [ -f $DHCP_HOOK_IFLIST ] && grep -qw $interface $DHCP_HOOK_IFLIST; }; then + return 0 +fi + +# Re-generate the config on the following events: +# - BOUND: always re-generate +# - RENEW: re-generate if the IP address changed +# - REBIND: re-generate if the IP address changed +if [ "$reason" == "RENEW" ] || [ "$reason" == "REBIND" ]; then + if [ "$old_ip_address" == "$new_ip_address" ]; then + return 0 + fi +elif [ "$reason" != "BOUND" ]; then + return 0 +fi + +# Best effort wait for any active commit to finish +sudo python3 - <<PYEND +from vyos.utils.commit import wait_for_commit_lock + +if __name__ == '__main__': + wait_for_commit_lock() + exit(0) +PYEND + +# Now re-generate the config +sudo /usr/libexec/vyos/conf_mode/vpn_ipsec.py diff --git a/src/etc/ipsec.d/key-pair.template b/src/etc/ipsec.d/key-pair.template new file mode 100644 index 0000000..56be975 --- /dev/null +++ b/src/etc/ipsec.d/key-pair.template @@ -0,0 +1,67 @@ +[ req ] + default_bits = 2048 + default_keyfile = privkey.pem + distinguished_name = req_distinguished_name + string_mask = utf8only + attributes = req_attributes + dirstring_type = nobmp +# SHA-1 is deprecated, so use SHA-2 instead. + default_md = sha256 +# Extension to add when the -x509 option is used. + x509_extensions = v3_ca + +[ req_distinguished_name ] + countryName = Country Name (2 letter code) + countryName_min = 2 + countryName_max = 2 + ST = State Name + localityName = Locality Name (eg, city) + organizationName = Organization Name (eg, company) + organizationalUnitName = Organizational Unit Name (eg, department) + commonName = Common Name (eg, Device hostname) + commonName_max = 64 + emailAddress = Email Address + emailAddress_max = 40 +[ req_attributes ] + challengePassword = A challenge password (optional) + challengePassword_min = 4 + challengePassword_max = 20 +[ v3_ca ] + subjectKeyIdentifier=hash + authorityKeyIdentifier=keyid:always,issuer:always + basicConstraints = critical, CA:true + keyUsage = critical, digitalSignature, cRLSign, keyCertSign +[ v3_intermediate_ca ] +# Extensions for a typical intermediate CA (`man x509v3_config`). + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer + basicConstraints = critical, CA:true, pathlen:0 + keyUsage = critical, digitalSignature, cRLSign, keyCertSign +[ usr_cert ] +# Extensions for client certificates (`man x509v3_config`). + basicConstraints = CA:FALSE + nsCertType = client, email + nsComment = "OpenSSL Generated Client Certificate" + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid,issuer + keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment + extendedKeyUsage = clientAuth, emailProtection +[ server_cert ] +# Extensions for server certificates (`man x509v3_config`). + basicConstraints = CA:FALSE + nsCertType = server + nsComment = "OpenSSL Generated Server Certificate" + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid,issuer:always + keyUsage = critical, digitalSignature, keyEncipherment + extendedKeyUsage = serverAuth +[ crl_ext ] +# Extension for CRLs (`man x509v3_config`). + authorityKeyIdentifier=keyid:always +[ ocsp ] +# Extension for OCSP signing certificates (`man ocsp`). + basicConstraints = CA:FALSE + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid,issuer + keyUsage = critical, digitalSignature + extendedKeyUsage = critical, OCSPSigning diff --git a/src/etc/ipsec.d/vti-up-down b/src/etc/ipsec.d/vti-up-down new file mode 100644 index 0000000..e1765ae --- /dev/null +++ b/src/etc/ipsec.d/vti-up-down @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# +# 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 +# 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/>. + +# Script called up strongswan to bring the VTI interface up/down based on +# the state of the IPSec tunnel. Called as vti_up_down vti_intf_name + +import os +import sys + +from syslog import syslog +from syslog import openlog +from syslog import LOG_PID +from syslog import LOG_INFO + +from vyos.configquery import ConfigTreeQuery +from vyos.configdict import get_interface_dict +from vyos.utils.commit import wait_for_commit_lock +from vyos.utils.process import call +from vyos.utils.vti_updown_db import open_vti_updown_db_for_update + +def supply_interface_dict(interface): + # Lazy-load the running config on first invocation + try: + conf = supply_interface_dict.cached_config + except AttributeError: + conf = ConfigTreeQuery() + supply_interface_dict.cached_config = conf + + _, vti = get_interface_dict(conf.config, ['interfaces', 'vti'], interface) + return vti + +if __name__ == '__main__': + verb = os.getenv('PLUTO_VERB') + connection = os.getenv('PLUTO_CONNECTION') + interface = sys.argv[1] + + if verb.endswith('-v6'): + protocol = 'v6' + else: + protocol = 'v4' + + openlog(ident=f'vti-up-down', logoption=LOG_PID, facility=LOG_INFO) + syslog(f'Interface {interface} {verb} {connection}') + + wait_for_commit_lock() + + if verb in ['up-client', 'up-client-v6', 'up-host', 'up-host-v6']: + with open_vti_updown_db_for_update() as db: + db.add(interface, connection, protocol) + db.commit(supply_interface_dict) + elif verb in ['down-client', 'down-client-v6', 'down-host', 'down-host-v6']: + with open_vti_updown_db_for_update() as db: + db.remove(interface, connection, protocol) + db.commit(supply_interface_dict) diff --git a/src/etc/logrotate.d/conntrackd b/src/etc/logrotate.d/conntrackd new file mode 100644 index 0000000..b0b09de --- /dev/null +++ b/src/etc/logrotate.d/conntrackd @@ -0,0 +1,9 @@ +/var/log/conntrackd-stats.log { + weekly + rotate 2 + missingok + + postrotate + systemctl restart conntrackd.service > /dev/null + endscript +} diff --git a/src/etc/logrotate.d/vyos-atop b/src/etc/logrotate.d/vyos-atop new file mode 100644 index 0000000..0c8359c --- /dev/null +++ b/src/etc/logrotate.d/vyos-atop @@ -0,0 +1,20 @@ +/var/log/atop/atop.log { + daily + dateext + dateformat _%Y-%m-%d_%H-%M-%S + maxsize 10M + missingok + nocompress + nocreate + nomail + rotate 10 + prerotate + # stop the service + systemctl stop atop.service + endscript + postrotate + # start atop service again + systemctl start atop.service + endscript +} + diff --git a/src/etc/logrotate.d/vyos-rsyslog b/src/etc/logrotate.d/vyos-rsyslog new file mode 100644 index 0000000..3c087b9 --- /dev/null +++ b/src/etc/logrotate.d/vyos-rsyslog @@ -0,0 +1,12 @@ +/var/log/messages { + create + missingok + nomail + notifempty + rotate 10 + size 1M + postrotate + # inform rsyslog service about rotation + /usr/lib/rsyslog/rsyslog-rotate + endscript +} diff --git a/src/etc/modprobe.d/ifb.conf b/src/etc/modprobe.d/ifb.conf new file mode 100644 index 0000000..2dcfb6a --- /dev/null +++ b/src/etc/modprobe.d/ifb.conf @@ -0,0 +1 @@ +options ifb numifbs=0 diff --git a/src/etc/modprobe.d/openvpn.conf b/src/etc/modprobe.d/openvpn.conf new file mode 100644 index 0000000..a9259fe --- /dev/null +++ b/src/etc/modprobe.d/openvpn.conf @@ -0,0 +1 @@ +blacklist ovpn-dco-v2 diff --git a/src/etc/netplug/linkup.d/vyos-python-helper b/src/etc/netplug/linkup.d/vyos-python-helper new file mode 100644 index 0000000..9c59c58 --- /dev/null +++ b/src/etc/netplug/linkup.d/vyos-python-helper @@ -0,0 +1,4 @@ +#!/bin/sh +PYTHON3=$(which python3) +# Call the real python script and forward commandline arguments +$PYTHON3 /etc/netplug/vyos-netplug-dhcp-client "${@:1}" diff --git a/src/etc/netplug/netplug b/src/etc/netplug/netplug new file mode 100644 index 0000000..60b65e8 --- /dev/null +++ b/src/etc/netplug/netplug @@ -0,0 +1,41 @@ +#!/bin/sh +# +# Copyright 2023 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/>. + +dev="$1" +action="$2" + +case "$action" in +in) + run-parts --arg $dev --arg in /etc/netplug/linkup.d + ;; +out) + run-parts --arg $dev --arg out /etc/netplug/linkdown.d + ;; + +# probe loads and initialises the driver for the interface and brings the +# interface into the "up" state, so that it can generate netlink(7) events. +# This interferes with "admin down" for an interface. Thus, commented out. An +# "admin up" is treated as a "link up" and thus, "link up" action is executed. +# To execute "link down" action on "admin down", run appropriate script in +# /etc/netplug/linkdown.d +#probe) +# ;; + +*) + exit 1 + ;; +esac diff --git a/src/etc/netplug/netplugd.conf b/src/etc/netplug/netplugd.conf new file mode 100644 index 0000000..7da3c67 --- /dev/null +++ b/src/etc/netplug/netplugd.conf @@ -0,0 +1,4 @@ +eth* +br* +bond* +wlan* diff --git a/src/etc/netplug/vyos-netplug-dhcp-client b/src/etc/netplug/vyos-netplug-dhcp-client new file mode 100644 index 0000000..55d15a1 --- /dev/null +++ b/src/etc/netplug/vyos-netplug-dhcp-client @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# +# Copyright 2023 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 sys + +from time import sleep + +from vyos.configquery import ConfigTreeQuery +from vyos.ifconfig import Section +from vyos.utils.boot import boot_configuration_complete +from vyos.utils.commit import commit_in_progress +from vyos.utils.process import call +from vyos import airbag +airbag.enable() + +if len(sys.argv) < 3: + airbag.noteworthy("Must specify both interface and link status!") + sys.exit(1) + +if not boot_configuration_complete(): + airbag.noteworthy("System bootup not yet finished...") + sys.exit(1) + +while commit_in_progress(): + sleep(1) + +interface = sys.argv[1] +in_out = sys.argv[2] +config = ConfigTreeQuery() + +interface_path = ['interfaces'] + Section.get_config_path(interface).split() + +for _, interface_config in config.get_config_dict(interface_path).items(): + # Bail out early if we do not have an IP address configured + if 'address' not in interface_config: + continue + # Bail out early if interface ist administrative down + if 'disable' in interface_config: + continue + systemd_action = 'start' + if in_out == 'out': + systemd_action = 'stop' + # Start/Stop DHCP service + if 'dhcp' in interface_config['address']: + call(f'systemctl {systemd_action} dhclient@{interface}.service') + # Start/Stop DHCPv6 service + if 'dhcpv6' in interface_config['address']: + call(f'systemctl {systemd_action} dhcp6c@{interface}.service') diff --git a/src/etc/opennhrp/opennhrp-script.py b/src/etc/opennhrp/opennhrp-script.py new file mode 100644 index 0000000..f6f6d07 --- /dev/null +++ b/src/etc/opennhrp/opennhrp-script.py @@ -0,0 +1,371 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021-2023 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 +import re +import sys +import vyos.ipsec + +from json import loads +from pathlib import Path + +from vyos.logger import getLogger +from vyos.utils.process import cmd +from vyos.utils.process import process_named_running + +NHRP_CONFIG: str = '/run/opennhrp/opennhrp.conf' + + +def vici_get_ipsec_uniqueid(conn: str, src_nbma: str, + dst_nbma: str) -> list[str]: + """ Find and return IKE SAs by src nbma and dst nbma + + Args: + conn (str): a connection name + src_nbma (str): an IP address of NBMA source + dst_nbma (str): an IP address of NBMA destination + + Returns: + list: a list of IKE connections that match a criteria + """ + if not conn or not src_nbma or not dst_nbma: + logger.error( + f'Incomplete input data for resolving IKE unique ids: ' + f'conn: {conn}, src_nbma: {src_nbma}, dst_nbma: {dst_nbma}') + return [] + + try: + logger.info( + f'Resolving IKE unique ids for: conn: {conn}, ' + f'src_nbma: {src_nbma}, dst_nbma: {dst_nbma}') + list_ikeid: list[str] = [] + list_sa: list = vyos.ipsec.get_vici_sas_by_name(conn, None) + for sa in list_sa: + if sa[conn]['local-host'].decode('ascii') == src_nbma \ + and sa[conn]['remote-host'].decode('ascii') == dst_nbma: + list_ikeid.append(sa[conn]['uniqueid'].decode('ascii')) + return list_ikeid + except Exception as err: + logger.error(f'Unable to find unique ids for IKE: {err}') + return [] + + +def vici_ike_terminate(list_ikeid: list[str]) -> bool: + """Terminating IKE SAs by list of IKE IDs + + Args: + list_ikeid (list[str]): a list of IKE ids to terminate + + Returns: + bool: result of termination action + """ + if not list: + logger.warning('An empty list for termination was provided') + return False + + try: + vyos.ipsec.terminate_vici_ikeid_list(list_ikeid) + return True + except Exception as err: + logger.error(f'Failed to terminate SA for IKE ids {list_ikeid}: {err}') + return False + + +def parse_type_ipsec(interface: str) -> tuple[str, str]: + """Get DMVPN Type and NHRP Profile from the configuration + + Args: + interface (str): a name of interface + + Returns: + tuple[str, str]: `peer_type` and `profile_name` + """ + if not interface: + logger.error('Cannot find peer type - no input provided') + return '', '' + + config_file: str = Path(NHRP_CONFIG).read_text() + regex: str = rf'^interface {interface} #(?P<peer_type>hub|spoke) ?(?P<profile_name>[^\n]*)$' + match = re.search(regex, config_file, re.M) + if match: + return match.groupdict()['peer_type'], match.groupdict()[ + 'profile_name'] + return '', '' + + +def add_peer_route(nbma_src: str, nbma_dst: str, mtu: str) -> None: + """Add a route to a NBMA peer + + Args: + nbma_src (str): a local IP address + nbma_dst (str): a remote IP address + mtu (str): a MTU for a route + """ + logger.info(f'Adding route from {nbma_src} to {nbma_dst} with MTU {mtu}') + # Find routes to a peer + route_get_cmd: str = f'sudo ip --json route get {nbma_dst} from {nbma_src}' + try: + route_info_data = loads(cmd(route_get_cmd)) + except Exception as err: + logger.error(f'Unable to find a route to {nbma_dst}: {err}') + return + + # Check if an output has an expected format + if not isinstance(route_info_data, list): + logger.error( + f'Garbage returned from the "{route_get_cmd}" ' + f'command: {route_info_data}') + return + + # Add static routes to a peer + for route_item in route_info_data: + route_dev = route_item.get('dev') + route_dst = route_item.get('dst') + route_gateway = route_item.get('gateway') + # Prepare a command to add a route + route_add_cmd = 'sudo ip route add' + if route_dst: + route_add_cmd = f'{route_add_cmd} {route_dst}' + if route_gateway: + route_add_cmd = f'{route_add_cmd} via {route_gateway}' + if route_dev: + route_add_cmd = f'{route_add_cmd} dev {route_dev}' + route_add_cmd = f'{route_add_cmd} proto 42 mtu {mtu}' + # Add a route + try: + cmd(route_add_cmd) + except Exception as err: + logger.error( + f'Unable to add a route using command "{route_add_cmd}": ' + f'{err}') + + +def vici_initiate(conn: str, child_sa: str, src_addr: str, + dest_addr: str) -> bool: + """Initiate IKE SA connection with specific peer + + Args: + conn (str): an IKE connection name + child_sa (str): a child SA profile name + src_addr (str): NBMA local address + dest_addr (str): NBMA address of a peer + + Returns: + bool: a result of initiation command + """ + logger.info( + f'Trying to initiate connection. Name: {conn}, child sa: {child_sa}, ' + f'src_addr: {src_addr}, dst_addr: {dest_addr}') + try: + vyos.ipsec.vici_initiate(conn, child_sa, src_addr, dest_addr) + return True + except Exception as err: + logger.error(f'Unable to initiate connection {err}') + return False + + +def vici_terminate(conn: str, src_addr: str, dest_addr: str) -> None: + """Find and terminate IKE SAs by local NBMA and remote NBMA addresses + + Args: + conn (str): IKE connection name + src_addr (str): NBMA local address + dest_addr (str): NBMA address of a peer + """ + logger.info( + f'Terminating IKE connection {conn} between {src_addr} ' + f'and {dest_addr}') + + ikeid_list: list[str] = vici_get_ipsec_uniqueid(conn, src_addr, dest_addr) + + if not ikeid_list: + logger.warning( + f'No active sessions found for IKE profile {conn}, ' + f'local NBMA {src_addr}, remote NBMA {dest_addr}') + else: + try: + vyos.ipsec.terminate_vici_ikeid_list(ikeid_list) + except Exception as err: + logger.error( + f'Failed to terminate SA for IKE ids {ikeid_list}: {err}') + +def iface_up(interface: str) -> None: + """Proceed tunnel interface UP event + + Args: + interface (str): an interface name + """ + if not interface: + logger.warning('No interface name provided for UP event') + + logger.info(f'Turning up interface {interface}') + try: + cmd(f'sudo ip route flush proto 42 dev {interface}') + cmd(f'sudo ip neigh flush dev {interface}') + except Exception as err: + logger.error( + f'Unable to flush route on interface "{interface}": {err}') + + +def peer_up(dmvpn_type: str, conn: str) -> None: + """Proceed NHRP peer UP event + + Args: + dmvpn_type (str): a type of peer + conn (str): an IKE profile name + """ + logger.info(f'Peer UP event for {dmvpn_type} using IKE profile {conn}') + src_nbma = os.getenv('NHRP_SRCNBMA') + dest_nbma = os.getenv('NHRP_DESTNBMA') + dest_mtu = os.getenv('NHRP_DESTMTU') + + if not src_nbma or not dest_nbma: + logger.error( + f'Can not get NHRP NBMA addresses: local {src_nbma}, ' + f'remote {dest_nbma}') + return + + logger.info(f'NBMA addresses: local {src_nbma}, remote {dest_nbma}') + if dest_mtu: + add_peer_route(src_nbma, dest_nbma, dest_mtu) + if conn and dmvpn_type == 'spoke' and process_named_running('charon'): + vici_terminate(conn, src_nbma, dest_nbma) + vici_initiate(conn, 'dmvpn', src_nbma, dest_nbma) + + +def peer_down(dmvpn_type: str, conn: str) -> None: + """Proceed NHRP peer DOWN event + + Args: + dmvpn_type (str): a type of peer + conn (str): an IKE profile name + """ + logger.info(f'Peer DOWN event for {dmvpn_type} using IKE profile {conn}') + + src_nbma = os.getenv('NHRP_SRCNBMA') + dest_nbma = os.getenv('NHRP_DESTNBMA') + + if not src_nbma or not dest_nbma: + logger.error( + f'Can not get NHRP NBMA addresses: local {src_nbma}, ' + f'remote {dest_nbma}') + return + + logger.info(f'NBMA addresses: local {src_nbma}, remote {dest_nbma}') + if conn and dmvpn_type == 'spoke' and process_named_running('charon'): + vici_terminate(conn, src_nbma, dest_nbma) + try: + cmd(f'sudo ip route del {dest_nbma} src {src_nbma} proto 42') + except Exception as err: + logger.error( + f'Unable to del route from {src_nbma} to {dest_nbma}: {err}') + + +def route_up(interface: str) -> None: + """Proceed NHRP route UP event + + Args: + interface (str): an interface name + """ + logger.info(f'Route UP event for interface {interface}') + + dest_addr = os.getenv('NHRP_DESTADDR') + dest_prefix = os.getenv('NHRP_DESTPREFIX') + next_hop = os.getenv('NHRP_NEXTHOP') + + if not dest_addr or not dest_prefix or not next_hop: + logger.error( + f'Can not get route details: dest_addr {dest_addr}, ' + f'dest_prefix {dest_prefix}, next_hop {next_hop}') + return + + logger.info( + f'Route details: dest_addr {dest_addr}, dest_prefix {dest_prefix}, ' + f'next_hop {next_hop}') + + try: + cmd(f'sudo ip route replace {dest_addr}/{dest_prefix} proto 42 \ + via {next_hop} dev {interface}') + cmd('sudo ip route flush cache') + except Exception as err: + logger.error( + f'Unable replace or flush route to {dest_addr}/{dest_prefix} ' + f'via {next_hop} dev {interface}: {err}') + + +def route_down(interface: str) -> None: + """Proceed NHRP route DOWN event + + Args: + interface (str): an interface name + """ + logger.info(f'Route DOWN event for interface {interface}') + + dest_addr = os.getenv('NHRP_DESTADDR') + dest_prefix = os.getenv('NHRP_DESTPREFIX') + + if not dest_addr or not dest_prefix: + logger.error( + f'Can not get route details: dest_addr {dest_addr}, ' + f'dest_prefix {dest_prefix}') + return + + logger.info( + f'Route details: dest_addr {dest_addr}, dest_prefix {dest_prefix}') + try: + cmd(f'sudo ip route del {dest_addr}/{dest_prefix} proto 42') + cmd('sudo ip route flush cache') + except Exception as err: + logger.error( + f'Unable delete or flush route to {dest_addr}/{dest_prefix}: ' + f'{err}') + + +if __name__ == '__main__': + logger = getLogger('opennhrp-script', syslog=True) + logger.debug( + f'Running script with arguments: {sys.argv}, ' + f'environment: {os.environ}') + + action = sys.argv[1] + interface = os.getenv('NHRP_INTERFACE') + + if not interface: + logger.error('Can not get NHRP interface name') + sys.exit(1) + + dmvpn_type, profile_name = parse_type_ipsec(interface) + if not dmvpn_type: + logger.info(f'Interface {interface} is not NHRP tunnel') + sys.exit() + + dmvpn_conn: str = '' + if profile_name: + dmvpn_conn: str = f'dmvpn-{profile_name}-{interface}' + if action == 'interface-up': + iface_up(interface) + elif action == 'peer-register': + pass + elif action == 'peer-up': + peer_up(dmvpn_type, dmvpn_conn) + elif action == 'peer-down': + peer_down(dmvpn_type, dmvpn_conn) + elif action == 'route-up': + route_up(interface) + elif action == 'route-down': + route_down(interface) + + sys.exit() diff --git a/src/etc/ppp/ip-down.d/98-vyos-pppoe-cleanup-nameservers b/src/etc/ppp/ip-down.d/98-vyos-pppoe-cleanup-nameservers new file mode 100644 index 0000000..5157469 --- /dev/null +++ b/src/etc/ppp/ip-down.d/98-vyos-pppoe-cleanup-nameservers @@ -0,0 +1,14 @@ +#!/bin/bash + +interface=$6 +if [ -z "$interface" ]; then + exit +fi + +if ! /usr/bin/systemctl -q is-active vyos-hostsd; then + exit # vyos-hostsd is not running +fi + +hostsd_client="/usr/bin/vyos-hostsd-client" +$hostsd_client --delete-name-servers --tag "dhcp-$interface" +$hostsd_client --apply diff --git a/src/etc/ppp/ip-up.d/96-vyos-sstpc-callback b/src/etc/ppp/ip-up.d/96-vyos-sstpc-callback new file mode 100644 index 0000000..4e8804f --- /dev/null +++ b/src/etc/ppp/ip-up.d/96-vyos-sstpc-callback @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 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/>. + +# This is a Python hook script which is invoked whenever a SSTP client session +# goes "ip-up". It will call into our vyos.ifconfig library and will then +# execute common tasks for the SSTP interface. The reason we have to "hook" this +# is that we can not create a sstpcX interface in advance in linux and then +# connect pppd to this already existing interface. + +from sys import argv +from sys import exit + +from vyos.configquery import ConfigTreeQuery +from vyos.configdict import get_interface_dict +from vyos.ifconfig import SSTPCIf + +# When the ppp link comes up, this script is called with the following +# parameters +# $1 the interface name used by pppd (e.g. ppp3) +# $2 the tty device name +# $3 the tty device speed +# $4 the local IP address for the interface +# $5 the remote IP address +# $6 the parameter specified by the 'ipparam' option to pppd + +if (len(argv) < 7): + exit(1) + +interface = argv[6] + +conf = ConfigTreeQuery() +_, sstpc = get_interface_dict(conf.config, ['interfaces', 'sstpc'], interface) + +# Update the config +p = SSTPCIf(interface) +p.update(sstpc) diff --git a/src/etc/ppp/ip-up.d/98-vyos-pppoe-setup-nameservers b/src/etc/ppp/ip-up.d/98-vyos-pppoe-setup-nameservers new file mode 100644 index 0000000..4affaeb --- /dev/null +++ b/src/etc/ppp/ip-up.d/98-vyos-pppoe-setup-nameservers @@ -0,0 +1,23 @@ +#!/bin/bash + +interface=$6 +if [ -z "$interface" ]; then + exit +fi + +if ! /usr/bin/systemctl -q is-active vyos-hostsd; then + exit # vyos-hostsd is not running +fi + +hostsd_client="/usr/bin/vyos-hostsd-client" + +$hostsd_client --delete-name-servers --tag "dhcp-$interface" + +if [ "$USEPEERDNS" ] && [ -n "$DNS1" ]; then +$hostsd_client --add-name-servers "$DNS1" --tag "dhcp-$interface" +fi +if [ "$USEPEERDNS" ] && [ -n "$DNS2" ]; then +$hostsd_client --add-name-servers "$DNS2" --tag "dhcp-$interface" +fi + +$hostsd_client --apply diff --git a/src/etc/ppp/ip-up.d/99-vyos-pppoe-callback b/src/etc/ppp/ip-up.d/99-vyos-pppoe-callback new file mode 100644 index 0000000..fa1917a --- /dev/null +++ b/src/etc/ppp/ip-up.d/99-vyos-pppoe-callback @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021-2022 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/>. + +# This is a Python hook script which is invoked whenever a PPPoE session goes +# "ip-up". It will call into our vyos.ifconfig library and will then execute +# common tasks for the PPPoE interface. The reason we have to "hook" this is +# that we can not create a pppoeX interface in advance in linux and then connect +# pppd to this already existing interface. + +from sys import argv +from sys import exit + +from vyos.configquery import ConfigTreeQuery +from vyos.configdict import get_interface_dict +from vyos.ifconfig import PPPoEIf + +# When the ppp link comes up, this script is called with the following +# parameters +# $1 the interface name used by pppd (e.g. ppp3) +# $2 the tty device name +# $3 the tty device speed +# $4 the local IP address for the interface +# $5 the remote IP address +# $6 the parameter specified by the 'ipparam' option to pppd + +if (len(argv) < 7): + exit(1) + +interface = argv[6] + +conf = ConfigTreeQuery() +_, pppoe = get_interface_dict(conf.config, ['interfaces', 'pppoe'], interface) + +# Update the config +p = PPPoEIf(interface) +p.update(pppoe) diff --git a/src/etc/rsyslog.conf b/src/etc/rsyslog.conf new file mode 100644 index 0000000..b3f41ac --- /dev/null +++ b/src/etc/rsyslog.conf @@ -0,0 +1,67 @@ +################# +#### MODULES #### +################# + +$ModLoad imuxsock # provides support for local system logging +$ModLoad imklog # provides kernel logging support (previously done by rklogd) +#$ModLoad immark # provides --MARK-- message capability + +$OmitLocalLogging off +$SystemLogSocketName /run/systemd/journal/syslog + +$KLogPath /proc/kmsg + +########################### +#### GLOBAL DIRECTIVES #### +########################### + +# Use traditional timestamp format. +# To enable high precision timestamps, comment out the following line. +# A modern-style logfile format similar to TraditionalFileFormat, buth with high-precision timestamps and timezone information +#$ActionFileDefaultTemplate RSYSLOG_FileFormat +# The "old style" default log file format with low-precision timestamps +$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat + +# Filter duplicated messages +$RepeatedMsgReduction on + +# +# Set the default permissions for all log files. +# +$FileOwner root +$FileGroup adm +$FileCreateMode 0640 +$DirCreateMode 0755 +$Umask 0022 + +# +# Stop excessive logging of sudo +# +:msg, contains, " pam_unix(sudo:session): session opened for user root(uid=0) by" stop +:msg, contains, "pam_unix(sudo:session): session closed for user root" stop + +# +# Include all config files in /etc/rsyslog.d/ +# +$IncludeConfig /etc/rsyslog.d/*.conf + +# The lines below cause all listed daemons/processes to be logged into +# /var/log/auth.log, then drops the message so it does not also go to the +# regular syslog so that messages are not duplicated + +$outchannel auth_log,/var/log/auth.log +if $programname == 'CRON' or + $programname == 'sudo' or + $programname == 'su' + then :omfile:$auth_log + +if $programname == 'CRON' or + $programname == 'sudo' or + $programname == 'su' + then stop + +############### +#### RULES #### +############### +# Emergencies are sent to everybody logged in. +*.emerg :omusrmsg:*
\ No newline at end of file diff --git a/src/etc/securetty b/src/etc/securetty new file mode 100644 index 0000000..17d8610 --- /dev/null +++ b/src/etc/securetty @@ -0,0 +1,83 @@ +# /etc/securetty: list of terminals on which root is allowed to login. +# See securetty(5) and login(1). +console + +# Standard serial ports +ttyS0 +ttyS1 + +# USB dongles +ttyUSB0 +ttyUSB1 +ttyUSB2 + +# Standard hypervisor virtual console +hvc0 + +# Oldstyle Xen console +xvc0 + +# Standard consoles +tty1 +tty2 +tty3 +tty4 +tty5 +tty6 +tty7 +tty8 +tty9 +tty10 +tty11 +tty12 +tty13 +tty14 +tty15 +tty16 +tty17 +tty18 +tty19 +tty20 +tty21 +tty22 +tty23 +tty24 +tty25 +tty26 +tty27 +tty28 +tty29 +tty30 +tty31 +tty32 +tty33 +tty34 +tty35 +tty36 +tty37 +tty38 +tty39 +tty40 +tty41 +tty42 +tty43 +tty44 +tty45 +tty46 +tty47 +tty48 +tty49 +tty50 +tty51 +tty52 +tty53 +tty54 +tty55 +tty56 +tty57 +tty58 +tty59 +tty60 +tty61 +tty62 +tty63 diff --git a/src/etc/security/capability.conf b/src/etc/security/capability.conf new file mode 100644 index 0000000..0a7235f --- /dev/null +++ b/src/etc/security/capability.conf @@ -0,0 +1,10 @@ +# this is a capability file (used in conjunction with the pam_cap.so module) + +# Special capability for Vyatta admin +all %vyattacfg + +# Vyatta Operator +cap_net_admin,cap_sys_boot,cap_audit_write %vyattaop + +## 'everyone else' gets no inheritable capabilities +none * diff --git a/src/etc/skel/.bashrc b/src/etc/skel/.bashrc new file mode 100644 index 0000000..ba7d500 --- /dev/null +++ b/src/etc/skel/.bashrc @@ -0,0 +1,119 @@ +# ~/.bashrc: executed by bash(1) for non-login shells. +# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc) +# for examples + +# If not running interactively, don't do anything +case $- in + *i*) ;; + *) return;; +esac + +# don't put duplicate lines or lines starting with space in the history. +# See bash(1) for more options +HISTCONTROL=ignoreboth + +# append to the history file, don't overwrite it +shopt -s histappend + +# for setting history length see HISTSIZE and HISTFILESIZE in bash(1) +HISTSIZE=1000 +HISTFILESIZE=2000 + +# check the window size after each command and, if necessary, +# update the values of LINES and COLUMNS. +shopt -s checkwinsize + +# If set, the pattern "**" used in a pathname expansion context will +# match all files and zero or more directories and subdirectories. +#shopt -s globstar + +# make less more friendly for non-text input files, see lesspipe(1) +#[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" + +# set variable identifying the chroot you work in (used in the prompt below) +if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then + debian_chroot=$(cat /etc/debian_chroot) +fi + +# set a fancy prompt (non-color, unless we know we "want" color) +case "$TERM" in + xterm-color) color_prompt=yes;; +esac + +# uncomment for a colored prompt, if the terminal has the capability; turned +# off by default to not distract the user: the focus in a terminal window +# should be on the output of commands, not on the prompt +#force_color_prompt=yes + +if [ -n "$force_color_prompt" ]; then + if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then + # We have color support; assume it's compliant with Ecma-48 + # (ISO/IEC-6429). (Lack of such support is extremely rare, and such + # a case would tend to support setf rather than setaf.) + color_prompt=yes + else + color_prompt= + fi +fi + +if [ "$color_prompt" = yes ]; then + PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\H${VRF:+(vrf:$VRF)}${NETNS:+(ns:$NETNS)}\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' +else + PS1='${debian_chroot:+($debian_chroot)}\u@\H${VRF:+:$VRF}${NETNS:+(ns:$NETNS)}:\w\$ ' +fi +unset color_prompt force_color_prompt + +# If this is an xterm set the title to user@host:dir +case "$TERM" in +xterm*|rxvt*) + PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\H: \w\a\]$PS1" + ;; +*) + ;; +esac + +# enable color support of ls and also add handy aliases +if [ -x /usr/bin/dircolors ]; then + test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" + alias ls='ls --color=auto' + #alias dir='dir --color=auto' + #alias vdir='vdir --color=auto' + + #alias grep='grep --color=auto' + #alias fgrep='fgrep --color=auto' + #alias egrep='egrep --color=auto' +fi + +# colored GCC warnings and errors +#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01' + +# some more ls aliases +#alias ll='ls -l' +#alias la='ls -A' +#alias l='ls -CF' + +# Alias definitions. +# You may want to put all your additions into a separate file like +# ~/.bash_aliases, instead of adding them here directly. +# See /usr/share/doc/bash-doc/examples in the bash-doc package. + +if [ -f ~/.bash_aliases ]; then + . ~/.bash_aliases +fi + +# enable programmable completion features (you don't need to enable +# this, if it's already enabled in /etc/bash.bashrc and /etc/profile +# sources /etc/bash.bashrc). +if ! shopt -oq posix; then + if [ -f /usr/share/bash-completion/bash_completion ]; then + . /usr/share/bash-completion/bash_completion + elif [ -f /etc/bash_completion ]; then + . /etc/bash_completion + fi +fi +OPAMROOT='/opt/opam'; export OPAMROOT; +OPAM_SWITCH_PREFIX='/opt/opam/4.07.0'; export OPAM_SWITCH_PREFIX; +CAML_LD_LIBRARY_PATH='/opt/opam/4.07.0/lib/stublibs:/opt/opam/4.07.0/lib/ocaml/stublibs:/opt/opam/4.07.0/lib/ocaml'; export CAML_LD_LIBRARY_PATH; +OCAML_TOPLEVEL_PATH='/opt/opam/4.07.0/lib/toplevel'; export OCAML_TOPLEVEL_PATH; +MANPATH=':/opt/opam/4.07.0/man'; export MANPATH; +PATH='/opt/opam/4.07.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'; export PATH; diff --git a/src/etc/skel/.profile b/src/etc/skel/.profile new file mode 100644 index 0000000..c9db459 --- /dev/null +++ b/src/etc/skel/.profile @@ -0,0 +1,22 @@ +# ~/.profile: executed by the command interpreter for login shells. +# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login +# exists. +# see /usr/share/doc/bash/examples/startup-files for examples. +# the files are located in the bash-doc package. + +# the default umask is set in /etc/profile; for setting the umask +# for ssh logins, install and configure the libpam-umask package. +#umask 022 + +# if running bash +if [ -n "$BASH_VERSION" ]; then + # include .bashrc if it exists + if [ -f "$HOME/.bashrc" ]; then + . "$HOME/.bashrc" + fi +fi + +# set PATH so it includes user's private bin if it exists +if [ -d "$HOME/bin" ] ; then + PATH="$HOME/bin:$PATH" +fi diff --git a/src/etc/sudoers.d/vyos b/src/etc/sudoers.d/vyos new file mode 100644 index 0000000..67d7bab --- /dev/null +++ b/src/etc/sudoers.d/vyos @@ -0,0 +1,63 @@ +# +# VyOS modifications to sudo configuration +# +Defaults syslog_goodpri=info +Defaults env_keep+=VYATTA_* + +# +# Command groups allowed for operator users +# +Cmnd_Alias IPTABLES = /sbin/iptables --list -n,\ + /sbin/iptables -L -vn,\ + /sbin/iptables -L * -vn,\ + /sbin/iptables -t * -L *, \ + /sbin/iptables -Z *,\ + /sbin/iptables -Z -t nat, \ + /sbin/iptables -t * -Z * +Cmnd_Alias IP6TABLES = /sbin/ip6tables -t * -Z *, \ + /sbin/ip6tables -t * -L * +Cmnd_Alias CONNTRACK = /usr/sbin/conntrack -L *, \ + /usr/sbin/conntrack -G *, \ + /usr/sbin/conntrack -E * +Cmnd_Alias IPFLUSH = /sbin/ip route flush cache, \ + /sbin/ip route flush cache *,\ + /sbin/ip neigh flush to *, \ + /sbin/ip neigh flush dev *, \ + /sbin/ip -f inet6 route flush cache, \ + /sbin/ip -f inet6 route flush cache *,\ + /sbin/ip -f inet6 neigh flush to *, \ + /sbin/ip -f inet6 neigh flush dev * +Cmnd_Alias ETHTOOL = /sbin/ethtool -p *, \ + /sbin/ethtool -S *, \ + /sbin/ethtool -a *, \ + /sbin/ethtool -c *, \ + /sbin/ethtool -i * +Cmnd_Alias DMIDECODE = /usr/sbin/dmidecode +Cmnd_Alias DISK = /usr/bin/lsof, /sbin/fdisk -l *, /sbin/sfdisk -d * +Cmnd_Alias DATE = /bin/date, /usr/sbin/ntpdate +Cmnd_Alias PPPOE_CMDS = /sbin/pppd, /sbin/poff, /usr/sbin/pppstats +Cmnd_Alias PCAPTURE = /usr/bin/tcpdump +Cmnd_Alias HWINFO = /usr/bin/lspci +Cmnd_Alias FORCE_CLUSTER = /usr/share/heartbeat/hb_takeover, \ + /usr/share/heartbeat/hb_standby +Cmnd_Alias DIAGNOSTICS = /bin/ip vrf exec * /bin/ping *, \ + /bin/ip vrf exec * /bin/traceroute *, \ + /bin/ip vrf exec * /usr/bin/mtr *, \ + /usr/libexec/vyos/op_mode/* +Cmnd_Alias KEA_IP6_ROUTES = /sbin/ip -6 route replace *,\ + /sbin/ip -6 route del * +%operator ALL=NOPASSWD: DATE, IPTABLES, ETHTOOL, IPFLUSH, HWINFO, \ + PPPOE_CMDS, PCAPTURE, /usr/sbin/wanpipemon, \ + DMIDECODE, DISK, CONNTRACK, IP6TABLES, \ + FORCE_CLUSTER, DIAGNOSTICS + +# Allow any user to run files in sudo-users +%users ALL=NOPASSWD: /opt/vyatta/bin/sudo-users/ + +# Allow members of group sudo to execute any command +%sudo ALL=NOPASSWD: ALL + +# Allow any user to query Machine Owner Key status +%sudo ALL=NOPASSWD: /usr/bin/mokutil + +_kea ALL=NOPASSWD: KEA_IP6_ROUTES diff --git a/src/etc/sysctl.d/30-vyos-router.conf b/src/etc/sysctl.d/30-vyos-router.conf new file mode 100644 index 0000000..76be41d --- /dev/null +++ b/src/etc/sysctl.d/30-vyos-router.conf @@ -0,0 +1,117 @@ +# +# VyOS specific sysctl settings, see sysctl.conf (5) for information. +# + +# Panic on OOPS +kernel.panic_on_oops=1 + +# Timeout before rebooting on panic +kernel.panic=60 + +# Send all core files to /var/core/core.program.pid.time +kernel.core_pattern=/var/core/core-%e-%p-%t + +# ARP configuration +# arp_filter - allow multiple network interfaces on same subnet +# arp_announce - avoid local addresses no on target's subnet +# arp_ignore - reply only if target IP is local_address on the interface + +# arp_filter defaults to 1 so set all to 0 so vrrp interfaces can override it. +net.ipv4.conf.all.arp_filter=0 + +# https://vyos.dev/T300 +net.ipv4.conf.all.arp_ignore=0 +net.ipv4.conf.all.arp_announce=2 + +# Enable packet forwarding for IPv4 +net.ipv4.ip_forward=1 + +# Enable directed broadcast forwarding feature described in rfc1812#section-5.3.5.2 and rfc2644. +# Note that setting the 'all' entry to 1 doesn't enable directed broadcast forwarding on all interfaces. +# To enable directed broadcast forwarding on an interface, both the 'all' entry and the input interface entry should be set to 1. +net.ipv4.conf.all.bc_forwarding=1 +net.ipv4.conf.default.bc_forwarding=0 + +# if a primary address is removed from an interface promote the +# secondary address if available +net.ipv4.conf.all.promote_secondaries=1 + +# Ignore ICMP broadcasts sent to broadcast/multicast +net.ipv4.icmp_echo_ignore_broadcasts=1 + +# Ignore bogus ICMP errors +net.ipv4.icmp_ignore_bogus_error_responses=1 + +# Send ICMP responses with primary address of exiting interface +net.ipv4.icmp_errors_use_inbound_ifaddr=1 + +# Log packets with impossible addresses to kernel log +net.ipv4.conf.all.log_martians=1 + +# Do not ignore all ICMP ECHO requests by default +net.ipv4.icmp_echo_ignore_all=0 + +# Disable source validation by default +net.ipv4.conf.all.rp_filter=0 +net.ipv4.conf.default.rp_filter=0 + +# Enable tcp syn-cookies by default +net.ipv4.tcp_syncookies=1 + +# Disable accept_redirects by default for any interface +net.ipv4.conf.all.accept_redirects=0 +net.ipv4.conf.default.accept_redirects=0 +net.ipv6.conf.all.accept_redirects=0 +net.ipv6.conf.default.accept_redirects=0 + +# Disable accept_source_route by default +net.ipv4.conf.all.accept_source_route=0 +net.ipv4.conf.default.accept_source_route=0 +net.ipv6.conf.all.accept_source_route=0 +net.ipv6.conf.default.accept_source_route=0 + +# Enable send_redirects by default +net.ipv4.conf.all.send_redirects=1 +net.ipv4.conf.default.send_redirects=1 + +# Increase size of buffer for netlink +net.core.rmem_max=2097152 + +# Remove IPv4 and IPv6 routes from forward information base when link goes down +net.ipv4.conf.all.ignore_routes_with_linkdown=1 +net.ipv4.conf.default.ignore_routes_with_linkdown=1 +net.ipv6.conf.all.ignore_routes_with_linkdown=1 +net.ipv6.conf.default.ignore_routes_with_linkdown=1 + +# Enable packet forwarding for IPv6 +net.ipv6.conf.all.forwarding=1 + +# Increase route table limit +net.ipv6.route.max_size = 262144 + +# Do not forget IPv6 addresses when a link goes down +net.ipv6.conf.default.keep_addr_on_down=1 +net.ipv6.conf.all.keep_addr_on_down=1 +net.ipv6.route.skip_notify_on_dev_down=1 + +# Default value of 20 seems to interfere with larger OSPF and VRRP setups +net.ipv4.igmp_max_memberships = 512 + +# Enable global RFS (Receive Flow Steering) configuration. RFS is inactive +# until explicitly configured at the interface level +net.core.rps_sock_flow_entries = 32768 + +# Congestion control +net.core.default_qdisc=fq_codel +net.ipv4.tcp_congestion_control=bbr + +# Disable IPv6 Segment Routing packets by default +net.ipv6.conf.all.seg6_enabled = 0 +net.ipv6.conf.default.seg6_enabled = 0 + +net.vrf.strict_mode = 1 + +# https://vyos.dev/T6570 +# By default, do not forward traffic from bridge to IPvX layer +net.bridge.bridge-nf-call-iptables = 0 +net.bridge.bridge-nf-call-ip6tables = 0
\ No newline at end of file diff --git a/src/etc/sysctl.d/31-vyos-addr_gen_mode.conf b/src/etc/sysctl.d/31-vyos-addr_gen_mode.conf new file mode 100644 index 0000000..07a0d15 --- /dev/null +++ b/src/etc/sysctl.d/31-vyos-addr_gen_mode.conf @@ -0,0 +1,14 @@ +### Added by vyos-1x ### +# +# addr_gen_mode - INTEGER +# Defines how link-local and autoconf addresses are generated. +# +# 0: generate address based on EUI64 (default) +# 1: do no generate a link-local address, use EUI64 for addresses generated +# from autoconf +# 2: generate stable privacy addresses, using the secret from +# stable_secret (RFC7217) +# 3: generate stable privacy addresses, using a random secret if unset +# +net.ipv6.conf.all.addr_gen_mode = 1 +net.ipv6.conf.default.addr_gen_mode = 1 diff --git a/src/etc/sysctl.d/32-vyos-podman.conf b/src/etc/sysctl.d/32-vyos-podman.conf new file mode 100644 index 0000000..7068bf8 --- /dev/null +++ b/src/etc/sysctl.d/32-vyos-podman.conf @@ -0,0 +1,5 @@ +# Increase inotify watchers as per https://bugzilla.redhat.com/show_bug.cgi?id=1829596 +fs.inotify.max_queued_events = 1048576 +fs.inotify.max_user_instances = 1048576 +fs.inotify.max_user_watches = 1048576 + diff --git a/src/etc/systemd/system-generators/vyos-generator b/src/etc/systemd/system-generators/vyos-generator new file mode 100644 index 0000000..34faab6 --- /dev/null +++ b/src/etc/systemd/system-generators/vyos-generator @@ -0,0 +1,94 @@ +#!/bin/sh +set -f + +LOG="" +DEBUG_LEVEL=1 +LOG_D="/run/vyos-router" +ENABLE="enabled" +DISABLE="disabled" +FOUND="found" +NOTFOUND="notfound" +RUN_ENABLED_FILE="$LOG_D/$ENABLE" +VYOS_SYSTEM_TARGET="/lib/systemd/system/vyos.target" +VYOS_TARGET_NAME="vyos.target" + +debug() { + local lvl="$1" + shift + [ "$lvl" -gt "$DEBUG_LEVEL" ] && return + if [ -z "$LOG" ]; then + local log="$LOG_D/${0##*/}.log" + { [ -d "$LOG_D" ] || mkdir -p "$LOG_D"; } && + { : > "$log"; } >/dev/null 2>&1 && LOG="$log" || + LOG="/dev/kmsg" + fi + echo "$@" >> "$LOG" +} + +default() { + _RET="$ENABLE" +} + +main() { + local normal_d="$1" early_d="$2" late_d="$3" + local target_name="multi-user.target" gen_d="$early_d" + local link_path="$gen_d/${target_name}.wants/${VYOS_TARGET_NAME}" + local ds="$NOTFOUND" + + debug 1 "$0 normal=$normal_d early=$early_d late=$late_d" + debug 2 "$0 $*" + + local search result="error" ret="" + for search in default; do + if $search; then + debug 1 "$search found $_RET" + [ "$_RET" = "$ENABLE" -o "$_RET" = "$DISABLE" ] && + result=$_RET && break + else + ret=$? + debug 0 "search $search returned $ret" + fi + done + + # enable AND ds=found == enable + # enable AND ds=notfound == disable + # disable || <any> == disabled + if [ "$result" = "$ENABLE" ]; then + if [ -e "$link_path" ]; then + debug 1 "already enabled: no change needed" + else + [ -d "${link_path%/*}" ] || mkdir -p "${link_path%/*}" || + debug 0 "failed to make dir $link_path" + if ln -snf "$VYOS_SYSTEM_TARGET" "$link_path"; then + debug 1 "enabled via $link_path -> $VYOS_SYSTEM_TARGET" + else + ret=$? + debug 0 "[$ret] enable failed:" \ + "ln $VYOS_SYSTEM_TARGET $link_path" + fi + fi + : > "$RUN_ENABLED_FILE" + elif [ "$result" = "$DISABLE" ]; then + if [ -f "$link_path" ]; then + if rm -f "$link_path"; then + debug 1 "disabled. removed existing $link_path" + else + ret=$? + debug 0 "[$ret] disable failed, remove $link_path" + fi + else + debug 1 "already disabled: no change needed [no $link_path]" + fi + if [ -e "$RUN_ENABLED_FILE" ]; then + rm -f "$RUN_ENABLED_FILE" + fi + else + debug 0 "unexpected result '$result' 'ds=$ds'" + ret=3 + fi + return $ret +} + +main "$@" + +# vi: ts=4 expandtab diff --git a/src/etc/systemd/system/ModemManager.service.d/override.conf b/src/etc/systemd/system/ModemManager.service.d/override.conf new file mode 100644 index 0000000..07a1846 --- /dev/null +++ b/src/etc/systemd/system/ModemManager.service.d/override.conf @@ -0,0 +1,7 @@ +[Unit] +After= +After=vyos-router.service + +[Service] +ExecStart= +ExecStart=/usr/sbin/ModemManager --filter-policy=strict --log-level=INFO --log-timestamps --log-journal diff --git a/src/etc/systemd/system/atop.service.d/10-override.conf b/src/etc/systemd/system/atop.service.d/10-override.conf new file mode 100644 index 0000000..10df158 --- /dev/null +++ b/src/etc/systemd/system/atop.service.d/10-override.conf @@ -0,0 +1,6 @@ +[Service] +ExecStartPre= +ExecStart= +ExecStart=/bin/sh -c 'exec /usr/bin/atop ${LOGOPTS} -w "${LOGPATH}/atop.log" ${LOGINTERVAL}' +ExecStartPost= + diff --git a/src/etc/systemd/system/certbot.service.d/10-override.conf b/src/etc/systemd/system/certbot.service.d/10-override.conf new file mode 100644 index 0000000..542f77e --- /dev/null +++ b/src/etc/systemd/system/certbot.service.d/10-override.conf @@ -0,0 +1,7 @@ +[Unit] +After= +After=vyos-router.service + +[Service] +ExecStart= +ExecStart=/usr/bin/certbot renew --config-dir /config/auth/letsencrypt --no-random-sleep-on-renew --post-hook "/usr/libexec/vyos/vyos-certbot-renew-pki.sh" diff --git a/src/etc/systemd/system/conntrackd.service.d/override.conf b/src/etc/systemd/system/conntrackd.service.d/override.conf new file mode 100644 index 0000000..eb611e0 --- /dev/null +++ b/src/etc/systemd/system/conntrackd.service.d/override.conf @@ -0,0 +1,8 @@ +[Unit] +After= +After=vyos-router.service +ConditionPathExists=/run/conntrackd/conntrackd.conf + +[Service] +ExecStart= +ExecStart=/usr/sbin/conntrackd -C /run/conntrackd/conntrackd.conf diff --git a/src/etc/systemd/system/conserver-server.service.d/override.conf b/src/etc/systemd/system/conserver-server.service.d/override.conf new file mode 100644 index 0000000..3c753f5 --- /dev/null +++ b/src/etc/systemd/system/conserver-server.service.d/override.conf @@ -0,0 +1,10 @@ +[Unit] +After= +After=vyos-router.service +ConditionPathExists=/run/conserver/conserver.cf + +[Service] +Type=simple +ExecStart= +ExecStart=/usr/sbin/conserver -M localhost -C /run/conserver/conserver.cf +Restart=on-failure diff --git a/src/etc/systemd/system/fastnetmon.service.d/override.conf b/src/etc/systemd/system/fastnetmon.service.d/override.conf new file mode 100644 index 0000000..8416660 --- /dev/null +++ b/src/etc/systemd/system/fastnetmon.service.d/override.conf @@ -0,0 +1,12 @@ +[Unit] +RequiresMountsFor=/run +ConditionPathExists=/run/fastnetmon/fastnetmon.conf +After= +After=vyos-router.service + +[Service] +Type=simple +WorkingDirectory=/run/fastnetmon +PIDFile=/run/fastnetmon.pid +ExecStart= +ExecStart=/usr/sbin/fastnetmon --configuration_file /run/fastnetmon/fastnetmon.conf diff --git a/src/etc/systemd/system/frr.service.d/override.conf b/src/etc/systemd/system/frr.service.d/override.conf new file mode 100644 index 0000000..614b4f7 --- /dev/null +++ b/src/etc/systemd/system/frr.service.d/override.conf @@ -0,0 +1,11 @@ +[Unit] +After=vyos-router.service + +[Service] +LimitNOFILE=4096 +ExecStartPre=/bin/bash -c 'mkdir -p /run/frr/config; \ + echo "log syslog" > /run/frr/config/frr.conf; \ + echo "log facility local7" >> /run/frr/config/frr.conf; \ + chown frr:frr /run/frr/config/frr.conf; \ + chmod 664 /run/frr/config/frr.conf; \ + mount --bind /run/frr/config/frr.conf /etc/frr/frr.conf' diff --git a/src/etc/systemd/system/getty@.service.d/aftervyos.conf b/src/etc/systemd/system/getty@.service.d/aftervyos.conf new file mode 100644 index 0000000..c575390 --- /dev/null +++ b/src/etc/systemd/system/getty@.service.d/aftervyos.conf @@ -0,0 +1,3 @@ +[Service] +ExecStartPre=-/usr/libexec/vyos/init/vyos-config +StandardOutput=journal+console diff --git a/src/etc/systemd/system/hostapd@.service.d/override.conf b/src/etc/systemd/system/hostapd@.service.d/override.conf new file mode 100644 index 0000000..926c07f --- /dev/null +++ b/src/etc/systemd/system/hostapd@.service.d/override.conf @@ -0,0 +1,12 @@ +[Unit] +After= +After=vyos-router.service +ConditionFileNotEmpty= +ConditionFileNotEmpty=/run/hostapd/%i.conf + +[Service] +WorkingDirectory=/run/hostapd +EnvironmentFile= +ExecStart= +ExecStart=/usr/sbin/hostapd -B -P /run/hostapd/%i.pid /run/hostapd/%i.conf +PIDFile=/run/hostapd/%i.pid diff --git a/src/etc/systemd/system/kea-ctrl-agent.service.d/override.conf b/src/etc/systemd/system/kea-ctrl-agent.service.d/override.conf new file mode 100644 index 0000000..0f5bf80 --- /dev/null +++ b/src/etc/systemd/system/kea-ctrl-agent.service.d/override.conf @@ -0,0 +1,9 @@ +[Unit] +After= +After=vyos-router.service + +[Service] +ExecStart= +ExecStart=/usr/sbin/kea-ctrl-agent -c /run/kea/kea-ctrl-agent.conf +AmbientCapabilities=CAP_NET_BIND_SERVICE +CapabilityBoundingSet=CAP_NET_BIND_SERVICE diff --git a/src/etc/systemd/system/kea-dhcp4-server.service.d/override.conf b/src/etc/systemd/system/kea-dhcp4-server.service.d/override.conf new file mode 100644 index 0000000..682e5bb --- /dev/null +++ b/src/etc/systemd/system/kea-dhcp4-server.service.d/override.conf @@ -0,0 +1,7 @@ +[Unit] +After= +After=vyos-router.service + +[Service] +ExecStart= +ExecStart=/usr/sbin/kea-dhcp4 -c /run/kea/kea-dhcp4.conf diff --git a/src/etc/systemd/system/kea-dhcp6-server.service.d/override.conf b/src/etc/systemd/system/kea-dhcp6-server.service.d/override.conf new file mode 100644 index 0000000..cb33fc0 --- /dev/null +++ b/src/etc/systemd/system/kea-dhcp6-server.service.d/override.conf @@ -0,0 +1,7 @@ +[Unit] +After= +After=vyos-router.service + +[Service] +ExecStart= +ExecStart=/usr/sbin/kea-dhcp6 -c /run/kea/kea-dhcp6.conf diff --git a/src/etc/systemd/system/logrotate.timer.d/10-override.conf b/src/etc/systemd/system/logrotate.timer.d/10-override.conf new file mode 100644 index 0000000..f50c2b0 --- /dev/null +++ b/src/etc/systemd/system/logrotate.timer.d/10-override.conf @@ -0,0 +1,2 @@ +[Timer] +OnCalendar=hourly diff --git a/src/etc/systemd/system/nginx.service.d/10-override.conf b/src/etc/systemd/system/nginx.service.d/10-override.conf new file mode 100644 index 0000000..1be5cec --- /dev/null +++ b/src/etc/systemd/system/nginx.service.d/10-override.conf @@ -0,0 +1,3 @@ +[Unit] +After= +After=vyos-router.service diff --git a/src/etc/systemd/system/ocserv.service.d/override.conf b/src/etc/systemd/system/ocserv.service.d/override.conf new file mode 100644 index 0000000..89dbb15 --- /dev/null +++ b/src/etc/systemd/system/ocserv.service.d/override.conf @@ -0,0 +1,14 @@ +[Unit] +RequiresMountsFor=/run +ConditionPathExists=/run/ocserv/ocserv.conf +After= +After=vyos-router.service +After=dbus.service + +[Service] +WorkingDirectory=/run/ocserv +PIDFile= +PIDFile=/run/ocserv/ocserv.pid +ExecStart= +ExecStart=/usr/sbin/ocserv --foreground --pid-file /run/ocserv/ocserv.pid --config /run/ocserv/ocserv.conf + diff --git a/src/etc/systemd/system/openvpn@.service.d/10-override.conf b/src/etc/systemd/system/openvpn@.service.d/10-override.conf new file mode 100644 index 0000000..775a2d7 --- /dev/null +++ b/src/etc/systemd/system/openvpn@.service.d/10-override.conf @@ -0,0 +1,14 @@ +[Unit] +After= +After=vyos-router.service + +[Service] +WorkingDirectory= +WorkingDirectory=/run/openvpn +ExecStart= +ExecStart=/usr/sbin/openvpn --daemon openvpn-%i --config %i.conf --status %i.status 30 --writepid %i.pid +ExecReload=/bin/kill -HUP $MAINPID +User=openvpn +Group=openvpn +AmbientCapabilities=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_AUDIT_WRITE +CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_AUDIT_WRITE diff --git a/src/etc/systemd/system/radvd.service.d/override.conf b/src/etc/systemd/system/radvd.service.d/override.conf new file mode 100644 index 0000000..812446d --- /dev/null +++ b/src/etc/systemd/system/radvd.service.d/override.conf @@ -0,0 +1,19 @@ +[Unit] +ConditionPathExists= +ConditionPathExists=/run/radvd/radvd.conf +After= +After=vyos-router.service + +[Service] +WorkingDirectory= +WorkingDirectory=/run/radvd +ExecStartPre= +ExecStartPre=/usr/sbin/radvd --logmethod stderr_clean --configtest --config /run/radvd/radvd.conf +ExecStart= +ExecStart=/usr/sbin/radvd --logmethod stderr_clean --config /run/radvd/radvd.conf --pidfile /run/radvd/radvd.pid +ExecReload= +ExecReload=/usr/sbin/radvd --logmethod stderr_clean --configtest --config /run/radvd/radvd.conf +ExecReload=/bin/kill -HUP $MAINPID +PIDFile= +PIDFile=/run/radvd/radvd.pid +Restart=always diff --git a/src/etc/systemd/system/serial-getty@.service.d/aftervyos.conf b/src/etc/systemd/system/serial-getty@.service.d/aftervyos.conf new file mode 100644 index 0000000..8ba4277 --- /dev/null +++ b/src/etc/systemd/system/serial-getty@.service.d/aftervyos.conf @@ -0,0 +1,3 @@ +[Service] +ExecStartPre=-/usr/libexec/vyos/init/vyos-config SERIAL +StandardOutput=journal+console diff --git a/src/etc/systemd/system/ssh@.service.d/vrf-override.conf b/src/etc/systemd/system/ssh@.service.d/vrf-override.conf new file mode 100644 index 0000000..b8952d8 --- /dev/null +++ b/src/etc/systemd/system/ssh@.service.d/vrf-override.conf @@ -0,0 +1,13 @@ +[Unit] +StartLimitIntervalSec=0 +After=vyos-router.service +ConditionPathExists=/run/sshd/sshd_config + +[Service] +EnvironmentFile= +ExecStart= +ExecStart=ip vrf exec %i /usr/sbin/sshd -f /run/sshd/sshd_config +Restart=always +RestartPreventExitStatus= +RestartSec=10 +RuntimeDirectoryPreserve=yes diff --git a/src/etc/systemd/system/suricata.service.d/10-override.conf b/src/etc/systemd/system/suricata.service.d/10-override.conf new file mode 100644 index 0000000..781256c --- /dev/null +++ b/src/etc/systemd/system/suricata.service.d/10-override.conf @@ -0,0 +1,9 @@ +[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/etc/systemd/system/wpa_supplicant-wired@.service.d/override.conf b/src/etc/systemd/system/wpa_supplicant-wired@.service.d/override.conf new file mode 100644 index 0000000..030b89a --- /dev/null +++ b/src/etc/systemd/system/wpa_supplicant-wired@.service.d/override.conf @@ -0,0 +1,11 @@ +[Unit] +After= +After=vyos-router.service + +[Service] +WorkingDirectory= +WorkingDirectory=/run/wpa_supplicant +PIDFile=/run/wpa_supplicant/%I.pid +ExecStart= +ExecStart=/sbin/wpa_supplicant -c/run/wpa_supplicant/%I.conf -Dwired -P/run/wpa_supplicant/%I.pid -i%I +ExecReload=/bin/kill -HUP $MAINPID diff --git a/src/etc/systemd/system/wpa_supplicant@.service.d/override.conf b/src/etc/systemd/system/wpa_supplicant@.service.d/override.conf new file mode 100644 index 0000000..5cffb79 --- /dev/null +++ b/src/etc/systemd/system/wpa_supplicant@.service.d/override.conf @@ -0,0 +1,11 @@ +[Unit] +After= +After=vyos-router.service + +[Service] +WorkingDirectory= +WorkingDirectory=/run/wpa_supplicant +PIDFile=/run/wpa_supplicant/%I.pid +ExecStart= +ExecStart=/sbin/wpa_supplicant -c/run/wpa_supplicant/%I.conf -Dnl80211,wext -P/run/wpa_supplicant/%I.pid -i%I +ExecReload=/bin/kill -HUP $MAINPID diff --git a/src/etc/telegraf/custom_scripts/show_firewall_input_filter.py b/src/etc/telegraf/custom_scripts/show_firewall_input_filter.py new file mode 100644 index 0000000..bb7515a --- /dev/null +++ b/src/etc/telegraf/custom_scripts/show_firewall_input_filter.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 + +import json +import re +import time + +from vyos.utils.process import cmd + + +def get_nft_filter_chains(): + """ + Get list of nft chains for table filter + """ + try: + nft = cmd('/usr/sbin/nft --json list table ip vyos_filter') + except Exception: + return [] + nft = json.loads(nft) + chain_list = [] + + for output in nft['nftables']: + if 'chain' in output: + chain = output['chain']['name'] + chain_list.append(chain) + + return chain_list + + +def get_nftables_details(name): + """ + Get dict, counters packets and bytes for chain + """ + command = f'/usr/sbin/nft list chain ip vyos_filter {name}' + try: + results = cmd(command) + except: + return {} + + # Trick to remove 'NAME_' from chain name in the comment + # It was added to any chain T4218 + # counter packets 0 bytes 0 return comment "FOO default-action accept" + comment_name = name.replace("NAME_", "") + out = {} + for line in results.split('\n'): + comment_search = re.search(rf'{comment_name}[\- ](\d+|default-action)', line) + if not comment_search: + continue + + rule = {} + rule_id = comment_search[1] + counter_search = re.search(r'counter packets (\d+) bytes (\d+)', line) + if counter_search: + rule['packets'] = counter_search[1] + rule['bytes'] = counter_search[2] + + rule['conditions'] = re.sub(r'(\b(counter packets \d+ bytes \d+|drop|reject|return|log)\b|comment "[\w\-]+")', '', line).strip() + out[rule_id] = rule + return out + + +def get_nft_telegraf(name): + """ + Get data for telegraf in influxDB format + """ + for rule, rule_config in get_nftables_details(name).items(): + print(f'nftables,table=vyos_filter,chain={name},' + f'ruleid={rule} ' + f'pkts={rule_config["packets"]}i,' + f'bytes={rule_config["bytes"]}i ' + f'{str(int(time.time()))}000000000') + + +chains = get_nft_filter_chains() + +for chain in chains: + get_nft_telegraf(chain) diff --git a/src/etc/telegraf/custom_scripts/show_interfaces_input_filter.py b/src/etc/telegraf/custom_scripts/show_interfaces_input_filter.py new file mode 100644 index 0000000..6f14d6a --- /dev/null +++ b/src/etc/telegraf/custom_scripts/show_interfaces_input_filter.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 + +from vyos.ifconfig import Section +from vyos.ifconfig import Interface + +import time + +def get_interface_addresses(iface, link_local_v6=False): + """ + Get IP and IPv6 addresses from interface in one string + By default don't get IPv6 link-local addresses + If interface doesn't have address, return "-" + """ + addresses = [] + addrs = Interface(iface).get_addr() + + for addr in addrs: + if link_local_v6 == False: + if addr.startswith('fe80::'): + continue + addresses.append(addr) + + if not addresses: + return "-" + + return (" ".join(addresses)) + +def get_interface_description(iface): + """ + Get interface description + If none return "empty" + """ + description = Interface(iface).get_alias() + + if not description: + return "empty" + + return description + +def get_interface_admin_state(iface): + """ + Interface administrative state + up => 0, down => 2 + """ + state = Interface(iface).get_admin_state() + if state == 'up': + admin_state = 0 + if state == 'down': + admin_state = 2 + + return admin_state + +def get_interface_oper_state(iface): + """ + Interface operational state + up => 0, down => 1 + """ + state = Interface(iface).operational.get_state() + if state == 'down': + oper_state = 1 + else: + oper_state = 0 + + return oper_state + +interfaces = Section.interfaces('') + +for iface in interfaces: + print(f'show_interfaces,interface={iface} ' + f'ip_addresses="{get_interface_addresses(iface)}",' + f'state={get_interface_admin_state(iface)}i,' + f'link={get_interface_oper_state(iface)}i,' + f'description="{get_interface_description(iface)}" ' + f'{str(int(time.time()))}000000000') diff --git a/src/etc/telegraf/custom_scripts/vyos_services_input_filter.py b/src/etc/telegraf/custom_scripts/vyos_services_input_filter.py new file mode 100644 index 0000000..00f2f18 --- /dev/null +++ b/src/etc/telegraf/custom_scripts/vyos_services_input_filter.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021-2023 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 time +from vyos.configquery import ConfigTreeQuery +from vyos.utils.process import is_systemd_service_running +from vyos.utils.process import process_named_running + +# Availible services and prouceses +# 1 - service +# 2 - process +services = { + "protocols bgp" : "bgpd", + "protocols ospf" : "ospfd", + "protocols ospfv3" : "ospf6d", + "protocols rip" : "ripd", + "protocols ripng" : "ripngd", + "protocols isis" : "isisd", + "service pppoe" : "accel-ppp@pppoe.service", + "vpn l2tp remote-access" : "accel-ppp@l2tp.service", + "vpn pptp remote-access" : "accel-ppp@pptp.service", + "vpn sstp" : "accel-ppp@sstp.service", + "vpn ipsec" : "charon" +} + +# Configured services +conf_services = { + 'zebra' : 0, + 'staticd' : 0, +} +# Get configured service and create list to check if process running +config = ConfigTreeQuery() +for service in services: + if config.exists(service): + conf_services[services[service]] = 0 + +for conf_service in conf_services: + status = 0 + if ".service" in conf_service: + # Check systemd service + if is_systemd_service_running(conf_service): + status = 1 + else: + # Check process + if process_named_running(conf_service): + status = 1 + print(f'vyos_services,service="{conf_service}" ' + f'status={str(status)}i {str(int(time.time()))}000000000') diff --git a/src/etc/udev/rules.d/42-qemu-usb.rules b/src/etc/udev/rules.d/42-qemu-usb.rules new file mode 100644 index 0000000..a79543d --- /dev/null +++ b/src/etc/udev/rules.d/42-qemu-usb.rules @@ -0,0 +1,14 @@ +# +# Enable autosuspend for qemu emulated usb hid devices. +# +# Note that there are buggy qemu versions which advertise remote +# wakeup support but don't actually implement it correctly. This +# is the reason why we need a match for the serial number here. +# The serial number "42" is used to tag the implementations where +# remote wakeup is working. +# +# Gerd Hoffmann <kraxel@xxxxxxxxxx> + +ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Mouse", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" +ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Tablet", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" +ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Keyboard", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" diff --git a/src/etc/udev/rules.d/62-temporary-interface-rename.rules b/src/etc/udev/rules.d/62-temporary-interface-rename.rules new file mode 100644 index 0000000..4a579dc --- /dev/null +++ b/src/etc/udev/rules.d/62-temporary-interface-rename.rules @@ -0,0 +1 @@ +SUBSYSTEM=="net", ACTION=="add", KERNEL=="eth*", DRIVERS=="?*", NAME="e$env{IFINDEX}" diff --git a/src/etc/udev/rules.d/63-hyperv-vf-net.rules b/src/etc/udev/rules.d/63-hyperv-vf-net.rules new file mode 100644 index 0000000..b4dcb5a --- /dev/null +++ b/src/etc/udev/rules.d/63-hyperv-vf-net.rules @@ -0,0 +1,5 @@ +ATTR{[dmi/id]sys_vendor}!="Microsoft Corporation", GOTO="end_hyperv_nic" + +ACTION=="add", SUBSYSTEM=="net", DRIVERS=="hv_pci", NAME="vf_%k" + +LABEL="end_hyperv_nic" diff --git a/src/etc/udev/rules.d/64-vyos-vmware-net.rules b/src/etc/udev/rules.d/64-vyos-vmware-net.rules new file mode 100644 index 0000000..66a4a06 --- /dev/null +++ b/src/etc/udev/rules.d/64-vyos-vmware-net.rules @@ -0,0 +1,14 @@ +ATTR{[dmi/id]sys_vendor}!="VMware, Inc.", GOTO="end_vmware_nic" + +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet0", ENV{VYOS_IFNAME}="eth0" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet1", ENV{VYOS_IFNAME}="eth1" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet2", ENV{VYOS_IFNAME}="eth2" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet3", ENV{VYOS_IFNAME}="eth3" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet4", ENV{VYOS_IFNAME}="eth4" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet5", ENV{VYOS_IFNAME}="eth5" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet6", ENV{VYOS_IFNAME}="eth6" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet7", ENV{VYOS_IFNAME}="eth7" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet8", ENV{VYOS_IFNAME}="eth8" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet9", ENV{VYOS_IFNAME}="eth9" + +LABEL="end_vmware_nic" diff --git a/src/etc/udev/rules.d/65-vyos-net.rules b/src/etc/udev/rules.d/65-vyos-net.rules new file mode 100644 index 0000000..32ae352 --- /dev/null +++ b/src/etc/udev/rules.d/65-vyos-net.rules @@ -0,0 +1,23 @@ +# These rules use vyos_net_name to persistently name network interfaces +# per "hwid" association in the VyOS configuration file. + +ACTION!="add", GOTO="vyos_net_end" +SUBSYSTEM!="net", GOTO="vyos_net_end" + +# Do name change for ethernet and wireless devices only +KERNEL!="eth*|wlan*|e*", GOTO="vyos_net_end" + +# ignore "secondary" monitor interfaces of mac80211 drivers +KERNEL=="wlan*", ATTRS{type}=="803", GOTO="vyos_net_end" + +# If using VyOS predefined names +ENV{VYOS_IFNAME}!="eth*", GOTO="end_vyos_predef_names" + +DRIVERS=="?*", PROGRAM="vyos_net_name %k $attr{address} $env{VYOS_IFNAME}", NAME="%c", GOTO="vyos_net_end" + +LABEL="end_vyos_predef_names" + +# ignore interfaces without a driver link like bridges and VLANs +DRIVERS=="?*", PROGRAM="vyos_net_name %k $attr{address}", NAME="%c" + +LABEL="vyos_net_end" diff --git a/src/etc/udev/rules.d/90-vyos-serial.rules b/src/etc/udev/rules.d/90-vyos-serial.rules new file mode 100644 index 0000000..30c1d31 --- /dev/null +++ b/src/etc/udev/rules.d/90-vyos-serial.rules @@ -0,0 +1,28 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="serial_end" +SUBSYSTEM!="tty", GOTO="serial_end" + +SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" +SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci" +SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" + +# /dev/serial/by-path/, /dev/serial/by-id/ for USB devices +KERNEL!="ttyUSB[0-9]*", GOTO="serial_end" + +SUBSYSTEMS=="usb-serial", ENV{.ID_PORT}="$attr{port_number}" + +IMPORT{builtin}="path_id", IMPORT{builtin}="usb_id" + +# Change the name of the usb id to a "more" human redable format. +# +# - $env{ID_PATH} usually is a name like: "pci-0000:00:10.0-usb-0:2.3.3.4:1.0-port0" so we strip the "pci-*" +# portion and only use the usb part +# - Transform the USB "speech" to the tree like structure so we start with "usb0" as root-complex 0. +# (tr -d -) does the replacement +# - Replace the first group after ":" to represent the bus relation (sed -e 0,/:/s//b/) indicated by "b" +# - Replace the next group after ":" to represent the port relation (sed -e 0,/:/s//p/) indicated by "p" +ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", PROGRAM="/bin/sh -c 'echo $env{ID_PATH} | cut -d- -f3- | tr -d - | sed -e 0,/:/s//b/ | sed -e 0,/:/s//p/'", SYMLINK+="serial/by-bus/$result" +ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", PROGRAM="/bin/sh -c 'echo $env{ID_PATH} | cut -d- -f3- | tr -d - | sed -e 0,/:/s//b/ | sed -e 0,/:/s//p/'", SYMLINK+="serial/by-bus/$result" + +LABEL="serial_end" diff --git a/src/etc/udev/rules.d/99-vyos-systemd.rules b/src/etc/udev/rules.d/99-vyos-systemd.rules new file mode 100644 index 0000000..54aea66 --- /dev/null +++ b/src/etc/udev/rules.d/99-vyos-systemd.rules @@ -0,0 +1,79 @@ +# The main reason that we store this file is systemd-udevd interfaces excludes +# /lib/systemd/systemd-sysctl for dynamic interfaces (ppp|ipoe|l2tp etc) + +ACTION=="remove", GOTO="systemd_end" + +SUBSYSTEM=="tty", KERNEL=="tty[a-zA-Z]*|hvc*|xvc*|hvsi*|ttysclp*|sclp_line*|3270/tty[0-9]*", TAG+="systemd" +KERNEL=="vport*", TAG+="systemd" + +SUBSYSTEM=="ptp", TAG+="systemd" + +SUBSYSTEM=="ubi", TAG+="systemd" + +SUBSYSTEM=="block", TAG+="systemd" + +# We can't make any conclusions about suspended DM devices so let's just import previous SYSTEMD_READY state and skip other rules +SUBSYSTEM=="block", ENV{DM_SUSPENDED}=="1", IMPORT{db}="SYSTEMD_READY", GOTO="systemd_end" +SUBSYSTEM=="block", ACTION=="add", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}="0" + +# Ignore encrypted devices with no identified superblock on it, since +# we are probably still calling mke2fs or mkswap on it. +SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0" + +# Explicitly set SYSTEMD_READY=1 for DM devices that don't have it set yet, so that we always have something to import above +SUBSYSTEM=="block", ENV{DM_UUID}=="?*", ENV{SYSTEMD_READY}=="", ENV{SYSTEMD_READY}="1" + +# add symlink to GPT root disk +SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}!="crypto_LUKS", SYMLINK+="gpt-auto-root" +SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}=="crypto_LUKS", SYMLINK+="gpt-auto-root-luks" +SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root", SYMLINK+="gpt-auto-root" + +# Ignore raid devices that are not yet assembled and started +SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", TEST!="md/array_state", ENV{SYSTEMD_READY}="0" +SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", ATTR{md/array_state}=="|clear|inactive", ENV{SYSTEMD_READY}="0" + +# Ignore loop devices that don't have any file attached +SUBSYSTEM=="block", KERNEL=="loop[0-9]*", ENV{DEVTYPE}=="disk", TEST!="loop/backing_file", ENV{SYSTEMD_READY}="0" + +# Ignore nbd devices until the PID file exists (which signals a connected device) +SUBSYSTEM=="block", KERNEL=="nbd*", ENV{DEVTYPE}=="disk", TEST!="pid", ENV{SYSTEMD_READY}="0" + +# We need a hardware independent way to identify network devices. We +# use the /sys/subsystem/ path for this. Kernel "bus" and "class" names +# should be treated as one namespace, like udev handles it. This is mostly +# just an identification string for systemd, so whether the path actually is +# accessible or not does not matter as long as it is unique and in the +# filesystem namespace. + +SUBSYSTEM=="net", KERNEL!="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/net/devices/$name" +SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/bluetooth/devices/%k", \ + ENV{SYSTEMD_WANTS}+="bluetooth.target", ENV{SYSTEMD_USER_WANTS}+="bluetooth.target" + +ENV{ID_SMARTCARD_READER}=="?*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="smartcard.target", ENV{SYSTEMD_USER_WANTS}+="smartcard.target" +SUBSYSTEM=="sound", KERNEL=="controlC*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sound.target", ENV{SYSTEMD_USER_WANTS}+="sound.target" + +SUBSYSTEM=="printer", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target", ENV{SYSTEMD_USER_WANTS}+="printer.target" +SUBSYSTEM=="usb", KERNEL=="lp*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target", ENV{SYSTEMD_USER_WANTS}+="printer.target" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target", ENV{SYSTEMD_USER_WANTS}+="printer.target" + +SUBSYSTEM=="udc", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}+="usb-gadget.target" + +# Apply sysctl variables to network devices (and only to those) as they appear. +# T5706. Exclude: lo, dummy*, ppp*, ipoe*, l2tp*, pptp*, sslvpn* and sstp*. +ACTION=="add", SUBSYSTEM=="net", KERNEL!="lo|dummy*|ppp*|ipoe*|l2tp*|pptp*|sslvpn*|sstp*", RUN+="/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/$name --prefix=/net/ipv4/neigh/$name --prefix=/net/ipv6/conf/$name --prefix=/net/ipv6/neigh/$name" + +# Pull in backlight save/restore for all backlight devices and +# keyboard backlights +SUBSYSTEM=="backlight", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_WANTS}+="systemd-backlight@backlight:$name.service" +SUBSYSTEM=="leds", KERNEL=="*kbd_backlight", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_WANTS}+="systemd-backlight@leds:$name.service" + +# Pull in rfkill save/restore for all rfkill devices +SUBSYSTEM=="rfkill", ENV{SYSTEMD_RFKILL}="1" +SUBSYSTEM=="rfkill", IMPORT{builtin}="path_id" +SUBSYSTEM=="misc", KERNEL=="rfkill", TAG+="systemd", ENV{SYSTEMD_WANTS}+="systemd-rfkill.socket" + +# Asynchronously mount file systems implemented by these modules as soon as they are loaded. +SUBSYSTEM=="module", KERNEL=="fuse", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-fs-fuse-connections.mount" +SUBSYSTEM=="module", KERNEL=="configfs", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-kernel-config.mount" + +LABEL="systemd_end" diff --git a/src/etc/update-motd.d/99-reboot b/src/etc/update-motd.d/99-reboot new file mode 100644 index 0000000..718be1a --- /dev/null +++ b/src/etc/update-motd.d/99-reboot @@ -0,0 +1,7 @@ +#!/bin/vbash +source /opt/vyatta/etc/functions/script-template +if [ -f /run/systemd/shutdown/scheduled ]; then + echo + run show reboot +fi +exit diff --git a/src/etc/vmware-tools/scripts/resume-vm-default.d/ether-resume.py b/src/etc/vmware-tools/scripts/resume-vm-default.d/ether-resume.py new file mode 100644 index 0000000..7da57bc --- /dev/null +++ b/src/etc/vmware-tools/scripts/resume-vm-default.d/ether-resume.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2018-2023 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 +import syslog + +from vyos import ConfigError +from vyos.config import Config +from vyos.utils.process import run + +def get_config(): + c = Config() + interfaces = dict() + for intf in c.list_effective_nodes('interfaces ethernet'): + # skip interfaces that are disabled + check_disable = f'interfaces ethernet {intf} disable' + if c.exists_effective(check_disable): + continue + + # get addresses configured on the interface + intf_addresses = c.return_effective_values( + f'interfaces ethernet {intf} address') + interfaces[intf] = [addr.strip("'") for addr in intf_addresses] + return interfaces + +def apply(config): + syslog.openlog(ident='ether-resume', logoption=syslog.LOG_PID, + facility=syslog.LOG_INFO) + + for intf, addresses in config.items(): + # bring the interface up + cmd = f'ip link set dev {intf} up' + syslog.syslog(cmd) + run(cmd) + + # add configured addresses to interface + for addr in addresses: + # dhcp is handled by netplug + if addr in ['dhcp', 'dhcpv6']: + continue + cmd = f'ip address add {addr} dev {intf}' + syslog.syslog(cmd) + run(cmd) + +if __name__ == '__main__': + try: + config = get_config() + apply(config) + except ConfigError as e: + print(e) + sys.exit(1) diff --git a/src/etc/vmware-tools/tools.conf b/src/etc/vmware-tools/tools.conf new file mode 100644 index 0000000..da98a4f --- /dev/null +++ b/src/etc/vmware-tools/tools.conf @@ -0,0 +1,2 @@ +[guestinfo] + poll-interval=30 |
