├── CHANGELOG.md ├── README.md ├── dispatcher └── 10trust ├── nmtrust └── ttoggle /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | 4 | ## [ 1.1.2 ] - 2020-11-18 5 | 6 | ### Fixed 7 | 8 | - Do not attempt to toggle undefined units 9 | 10 | 11 | ## [ 1.1.1 ] - 2020-10-01 12 | 13 | ### Changed 14 | 15 | - Catch undefined options 16 | - Ignore lines beginning with `#` in trusted units file 17 | 18 | ## [ 1.1.0 ] - 2018-11-24 19 | 20 | ### Changed 21 | 22 | - Use `-t` instead of `-f` to specify an alternative location for the trusted networks file 23 | - Move default trusted unit and trusted network files to `/etc/nmtrust/`. Existing users should simply move their files to upgrade: 24 | ``` 25 | # mkdir /etc/nmtrust 26 | # mv /usr/local/etc/trusted_units /etc/nmtrust/ 27 | # mv /usr/local/etc/trusted_networks /etc/nmtrust/ 28 | ``` 29 | 30 | ### Fixed 31 | 32 | - Properly process network names that contain whitespace 33 | 34 | ### Added 35 | 36 | - Exclude networks by name, e.g. docker networks 37 | 38 | 39 | ## [ 1.0.0 ] - 2018-07-23 40 | 41 | - Initial release 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nmtrust 2 | 3 | This project provides a simple framework for determining the trusted state of the 4 | current network connections, and taking action based on the result. It is 5 | intended to be used to activate certain services on trusted networks, and 6 | disable them when when there is a connection to an untrusted network or when 7 | there is no established network connection. 8 | 9 | ## Requirements 10 | 11 | * [NetworkManager](https://wiki.gnome.org/Projects/NetworkManager) 12 | * [sudo](https://www.sudo.ws/) 13 | 14 | ## Defining Trust 15 | 16 | NetworkManager assigns a UUID to each network profile. These can be seen by 17 | running `nmcli conn`. The UUIDs of the trusted networks should be placed in a 18 | file. By default `nmtrust` will look for this file at 19 | `/etc/nmtrust/trusted_networks`, however an alternative location may be 20 | provided using the `-t` option. 21 | 22 | If all of the current network connections are trusted, the trusted network file 23 | can be initiated with these values. 24 | 25 | # nmcli --terse -f uuid conn show --active > /etc/nmtrust/trusted_networks 26 | 27 | `nmtrust` will ask NetworkManager for a list of all active connections. It will 28 | then compare the UUIDs of the active connections against the trusted network 29 | file. 30 | 31 | * If **all** the current connections are matched in the trusted network file, 32 | `nmtrust` will report that all connections are trusted. 33 | * If **none** of the current connections exist in the trusted network file, 34 | `nmtrust` will report that all connections are untrusted. 35 | * If **some** of the current connections exist in the trusted network file, but 36 | some do not, `nmtrust` will report that one or more connections are 37 | untrusted. 38 | * If there are no active network connections, `nmtrust` will report this. 39 | 40 | ### Exclude Networks 41 | 42 | Network connections can be excluded from nmtrust as well. 43 | 44 | For example, if you have Docker installed, the Docker bridge network connection(s) 45 | needs to be excluded from the active network connections list. Otherwise, if you 46 | disconnect all other connections, `nmtrust` still thinks there are active connections 47 | despite that you are offline. 48 | 49 | The name of the network(s) that need to be excluded should be placed in 50 | `/etc/nmtrust/excluded_networks`, however an alternative location may be 51 | provided using the `-e` option. 52 | 53 | You can place the exact names in the file or you can use wildcards to exclude multiple 54 | networks. For example, `virbr0`, `virbr1`, etc. pp. or just `virbr?`. You can also 55 | specify a range: `virbr[0,1]`. 56 | 57 | ### Usage 58 | 59 | A unique exit code is returned for each of the four possible states. 60 | 61 | Exit Code | State 62 | --------- | ----- 63 | 0 | All connections are trusted 64 | 2 | All connections are untrusted 65 | 3 | One or more connections are untrusted 66 | 4 | There are no active connections 67 | 68 | This allows the user to easily script `nmtrust` to only execute certain actions 69 | on certain types of networks. For example, you may have a network backup script 70 | `netbackup.sh` that is executed every hour by cron. However, you only want the 71 | script to run when you are connected solely to a network or networks that you 72 | trust. This is easy to accomplish by creating a wrapper around `netbackup.sh` 73 | for cron to call. 74 | 75 | ``` 76 | #!/bin/sh 77 | 78 | # Execute nmtrust 79 | nmtrust 80 | 81 | # Execute backups if the current connection(s) are trusted. 82 | if [ $? -eq 0 ]; then 83 | netbackup.sh 84 | fi 85 | ``` 86 | 87 | ## systemd integration 88 | 89 | While `nmtrust` is a flexible script that can run anywhere NetworkManager is 90 | present, `ttoggle` is provided for use on systems with 91 | [systemd](https://wiki.freedesktop.org/www/Software/systemd/). 92 | 93 | The idea here is that the user has a number of systemd units that they only 94 | want to start when connected to a trusted network. The name of the trusted 95 | units should be placed in a file, one per line. By default `ttoggle` will look 96 | for this file at `/etc/nmtrust/trusted_units`, however an alternative location 97 | may be provided using the `-f` option. 98 | 99 | When `ttoggle` is executed, it calls `nmtrust` to determine the state of the 100 | network connections. If `nmtrust` reports that all the current connections are 101 | trusted, `ttoggle` will start all the units listed in the trusted unit file. If 102 | `nmtrust` reports that there is a connection to an untrusted network or that 103 | the system is offline, `ttoggle` will stop all the units listed in the trusted 104 | unit file. 105 | 106 | ### Usage 107 | 108 | The user may have a 109 | [timer](http://www.freedesktop.org/software/systemd/man/systemd.timer.html) to 110 | periodically send and receive mail, and a service that provides an IRC instant 111 | messaging gateway. These may both potentially leak personal information over 112 | the network, so they should not be started on untrusted connections. 113 | 114 | # echo 'mailsync.timer\nircgateway.service' > /etc/nmtrust/trusted_units 115 | 116 | Now when `ttoggle` is called it will start or stop these trusted units as 117 | appropriate. 118 | 119 | 120 | #### Status 121 | 122 | The `-s` option may be used to see an abbreviated status of all the trusted 123 | units. 124 | 125 | $ ttoggle -s 126 | 127 | 128 | #### Stop Everything 129 | 130 | The `-x` option may be used to stop all of the trusted units, regardless of the 131 | network trust. 132 | 133 | $ ttoggle -x 134 | 135 | 136 | #### Start Everything 137 | 138 | The `-t` option may be used to start all of the trusted units, regardless of 139 | the network trust. This may be useful for temporarily trusting a network 140 | connection. 141 | 142 | $ ttoggle -t 143 | 144 | 145 | ### Allow Offline 146 | 147 | There may be some units that should be run on trusted networks *and* when there 148 | is no network connection, but not when connected to an untrusted network. For 149 | example, the [git-annex assistant](https://git-annex.branchable.com/assistant/) 150 | provides useful functionality both online and offline, but may leak personal 151 | information (such as the location of networked remotes) on untrusted networks. 152 | These units can be allowed to run offline by adding `,allow_offline` to the 153 | unit entry in the trusted unit file. 154 | 155 | # echo 'git-annex@user.service,allow_offline' >> /etc/nmtrust/trusted_units' 156 | 157 | When `ttoggle` is called it will now perform the following: 158 | 159 | * Start all units when connected to trusted networks. 160 | * Stop all units when connected to untrusted networks. 161 | * Stop all units when connected to no network, and then start units that are 162 | marked `allow_offline`. 163 | 164 | 165 | ### User Units 166 | 167 | User units may be specified by adding `,user:username` to the unit entry in the 168 | trusted unit file. For example, if the user `pigmonkey` has a unit 169 | `ssh-tunnel.service` that should only be started on trusted networks: 170 | 171 | # echo 'ssh-tunnel.service,user:pigmonkey' >> /etc/nmtrust/trusted_units 172 | 173 | When starting, stopping, or checking the status of these units `ttoggle` will 174 | check if the calling user is the same as the user specified for the unit. If 175 | the users match, the current user will be used to take the appropriate action. 176 | If the users do not match (for instance, when `ttoggle` is called by root), 177 | `sudo` will be used to take action as the specified user. 178 | 179 | ### Automation 180 | 181 | A NetworkManager dispatcher is provided to automate the toggling of trusted 182 | units. Once installed, the dispatcher will cause NetworkManager to call 183 | `ttoggle` whenever a network connection is activated or deactived. 184 | 185 | # cp dispatcher/10trust /etc/NetworkManager/dispatcher.d 186 | # chmod 755 /etc/NetworkManager/dispatcher.d/10trust 187 | -------------------------------------------------------------------------------- /dispatcher/10trust: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Toggle trusted units whenever a connection is activated or deactived. 3 | 4 | action="$2" 5 | 6 | case $action in 7 | up) 8 | ttoggle 9 | ;; 10 | down) 11 | ttoggle 12 | ;; 13 | esac 14 | 15 | exit $? 16 | -------------------------------------------------------------------------------- /nmtrust: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | EXCLUDEFILE="/etc/nmtrust/excluded_networks" 4 | TRUSTFILE="/etc/nmtrust/trusted_networks" 5 | 6 | ############################################################################### 7 | 8 | # Use colors, but only if connected to a terminal, and that terminal supports them. 9 | if [ -n "$TERM" ] && which tput >/dev/null 2>&1; then 10 | ncolors=$(tput colors) 11 | fi 12 | if [ -t 1 ] && [ -n "$ncolors" ] && [ "$ncolors" -ge 8 ]; then 13 | RED="$(tput setaf 1)" 14 | GREEN="$(tput setaf 2)" 15 | YELLOW="$(tput setaf 3)" 16 | NORMAL="$(tput sgr0)" 17 | else 18 | RED="" 19 | GREEN="" 20 | YELLOW="" 21 | NORMAL="" 22 | fi 23 | 24 | usage() { 25 | echo "Usage: nmtrust [OPTION...] 26 | Determine if the current NetworkManager connections are trusted. 27 | 28 | Options: 29 | -e specify an alternative location for the excluded networks file 30 | -t specify an alternative location for the trusted networks file 31 | -q be quiet 32 | -v be verbose" 33 | } 34 | 35 | message() { 36 | if [ "$quiet" != true ]; then 37 | echo "$@" 38 | fi 39 | } 40 | 41 | file_check() { 42 | excludefile_dir=$(dirname "$EXCLUDEFILE") 43 | if [ ! -d "$excludefile_dir" ]; then 44 | if mkdir -p "$excludefile_dir"; then 45 | message "Created configuration directory: $excludefile_dir" 46 | else 47 | message "Failed to create configuration directory: $excludefile_dir" 48 | exit 1 49 | fi 50 | fi 51 | if [ ! -f "$EXCLUDEFILE" ]; then 52 | if touch $EXCLUDEFILE; then 53 | message "Created empty excluded networks file: $EXCLUDEFILE" 54 | else 55 | message "Failed to create excluded networks file: $EXCLUDEFILE" 56 | exit 1 57 | fi 58 | fi 59 | if [ ! -f "$TRUSTFILE" ]; then 60 | message "Could not locate trusted networks file: $TRUSTFILE" 61 | exit 1 62 | fi 63 | } 64 | 65 | check_connection() { 66 | local name=$1 67 | local connection_excluded=false 68 | mapfile -t excludes < <(grep -v '^#' < $EXCLUDEFILE) 69 | for exclude in "${excludes[@]}"; do 70 | # NOTE: Cannot quote right-hand site of == because glob matching is needed [shellcheck(SC2053)] 71 | if [[ "$name" == $exclude ]]; then 72 | connection_excluded=true 73 | break 74 | fi 75 | done 76 | echo $connection_excluded 77 | } 78 | 79 | list_connections() { 80 | mapfile -t nmcli < <(nmcli conn show --active | grep -v loopback) 81 | 82 | for (( i=0; i<${#nmcli[@]}; i++ )); do 83 | if [ "$i" -eq 0 ]; then 84 | echo "${nmcli[$i]}STATUS" 85 | else 86 | name=$(echo "${connections[$i-1]}" | awk -F ":" '{print $1}') 87 | uuid=$(echo "${connections[$i-1]}" | awk -F ":" '{print $2}') 88 | if grep -q "$uuid" "$TRUSTFILE"; then 89 | echo "${GREEN}${nmcli[$i]}trusted${NORMAL}" 90 | elif [[ $(check_connection "$name") = true ]]; then 91 | echo "${GREEN}${nmcli[$i]}excluded${NORMAL}" 92 | else 93 | echo "${RED}${nmcli[$i]}untrusted${NORMAL}" 94 | fi 95 | fi 96 | done 97 | } 98 | 99 | trusted() { 100 | message "${GREEN}All connections are trusted${NORMAL}" 101 | if [ "$verbose" == true ]; then 102 | echo 103 | list_connections 104 | fi 105 | exit 0 106 | } 107 | 108 | all_untrusted() { 109 | message "${RED}All connections are untrusted${NORMAL}" 110 | if [ "$verbose" == true ]; then 111 | echo 112 | list_connections 113 | fi 114 | exit 2 115 | } 116 | 117 | untrusted() { 118 | message "${RED}${1-One or more} connections are untrusted${NORMAL}" 119 | if [ "$verbose" == true ]; then 120 | echo 121 | list_connections 122 | fi 123 | exit 3 124 | } 125 | 126 | no_network() { 127 | message "${YELLOW}There are no active connections${NORMAL}" 128 | exit 4 129 | } 130 | 131 | while getopts ":e:t:qvh" opt; do 132 | case $opt in 133 | e) 134 | EXCLUDEFILE=$OPTARG 135 | ;; 136 | t) 137 | TRUSTFILE=$OPTARG 138 | ;; 139 | q) 140 | quiet=true 141 | ;; 142 | v) 143 | verbose=true 144 | ;; 145 | :) 146 | echo "Option -$OPTARG requires an argument." 147 | usage 148 | exit 1 149 | ;; 150 | h | *) 151 | usage 152 | exit 153 | ;; 154 | esac 155 | done 156 | 157 | # Check if the excluded and trusted networks files exist. 158 | file_check 159 | 160 | # Get all active connections. 161 | mapfile -t connections < <(nmcli --terse -f name,uuid,type conn show --active | grep -v loopback) 162 | 163 | # Get number of active connections. 164 | num_connections=0 165 | for connection in "${connections[@]}"; do 166 | name=$(echo "$connection" | awk -F ":" '{print $1}') 167 | if [[ $(check_connection "$name") = false ]]; then 168 | ((num_connections++)) 169 | fi 170 | done 171 | 172 | # Get number of trusted connections. 173 | num_trusted=$(comm -12 <(nmcli --terse -f uuid,type conn show --active | grep -v loopback | sed -e 's/:.*$//' | sort) <(sort "$TRUSTFILE") | wc -l) 174 | 175 | # Determine if there are active connections. 176 | if [ "$num_connections" -eq 0 ]; then 177 | no_network 178 | # Check if any of the active connections are untrusted. 179 | elif [[ "$num_trusted" -eq 0 ]]; then 180 | all_untrusted 181 | else 182 | for connection in "${connections[@]}"; do 183 | name=$(echo "$connection" | awk -F ":" '{print $1}') 184 | uuid=$(echo "$connection" | awk -F ":" '{print $2}') 185 | if [[ $(check_connection "$name") = false ]] && ! grep -q ^"$uuid"$ "$TRUSTFILE"; then 186 | num_untrusted=$((num_connections - num_trusted)) 187 | untrusted "$num_untrusted of $num_connections" 188 | fi 189 | done 190 | fi 191 | # If we're still here, all connections are trusted. 192 | trusted 193 | -------------------------------------------------------------------------------- /ttoggle: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | UNITFILE="/etc/nmtrust/trusted_units" 4 | 5 | ############################################################################### 6 | 7 | usage() { 8 | echo "Usage: ttrust [OPTION...] 9 | Toggle the activation of certain systemd units based on the trust of the current network connections. 10 | 11 | Options: 12 | -f specify an alternative location for the trusted unit file 13 | -s display the status of the trusted units and exit 14 | -x stop all trusted units, regardless of network trust 15 | -t start all trusted units, regardless of network trust 16 | -q be quiet" 17 | } 18 | 19 | file_check() { 20 | if [ ! -f "$UNITFILE" ]; then 21 | if [ "$quiet" != true ]; then 22 | echo "Could not locate trusted unit file: $UNITFILE" 23 | fi 24 | exit 1 25 | fi 26 | } 27 | 28 | find_nmtrust() { 29 | if hash nmtrust 2> /dev/null; then 30 | NMTRUST=nmtrust 31 | else 32 | echo "Could not find nmtrust" 33 | exit 127 34 | fi 35 | } 36 | 37 | extract_user() { 38 | echo "$1" | sed 's/.*user:\([^,]*\).*/\1/' 39 | } 40 | 41 | get_trusted_units() { 42 | TRUSTED_SYSTEM_UNITS=$(grep -v '^#\|,.*user:' "$UNITFILE" | cut -d ',' -f1) 43 | OFFLINE_SYSTEM_UNITS=$(grep -v '^#\|,.*user:' "$UNITFILE" | grep ',.*allow_offline' | cut -d ',' -f1) 44 | TRUSTED_USER_UNITS=$(grep -v '^#' "$UNITFILE" | grep ',.*user:') 45 | OFFLINE_USER_UNITS=$(grep -v '^#' "$UNITFILE" | grep ',.*user:' | grep ',.*allow_offline') 46 | } 47 | 48 | user_toggle() { 49 | unit_user=$(extract_user "$line") 50 | unit=$(echo "$line" | cut -d ',' -f1) 51 | if [ "$1" = "status" ]; then 52 | command="SYSTEMD_COLORS=1 systemctl $1 --user $unit | sed '1p;/^\s*Active:/!d'" 53 | else 54 | command="systemctl $1 --user $unit" 55 | fi 56 | if [ "$unit_user" = "$USER" ]; then 57 | eval "$command" 58 | else 59 | sudo -u "$unit_user" bash -c "export XDG_RUNTIME_DIR=/run/user/$(id -u "$unit_user"); $command" 60 | fi 61 | } 62 | 63 | start() { 64 | if [ -n "$TRUSTED_SYSTEM_UNITS" ]; then 65 | if [ "$quiet" != true ]; then 66 | echo "Starting trusted system units" 67 | fi 68 | systemctl start $TRUSTED_SYSTEM_UNITS 69 | fi 70 | if [ -n "$TRUSTED_USER_UNITS" ]; then 71 | if [ "$quiet" != true ]; then 72 | echo "Starting trusted user units" 73 | fi 74 | echo "$TRUSTED_USER_UNITS" | while read -r line; do 75 | user_toggle "start" "$line" 76 | done 77 | fi 78 | } 79 | 80 | stop() { 81 | if [ -n "$TRUSTED_SYSTEM_UNITS" ]; then 82 | if [ "$quiet" != true ]; then 83 | echo "Stopping trusted system units" 84 | fi 85 | systemctl stop $TRUSTED_SYSTEM_UNITS 86 | fi 87 | if [ -n "$TRUSTED_USER_UNITS" ]; then 88 | if [ "$quiet" != true ]; then 89 | echo "Stopping trusted user units" 90 | fi 91 | echo "$TRUSTED_USER_UNITS" | while read -r line; do 92 | user_toggle "stop" "$line" 93 | done 94 | fi 95 | } 96 | 97 | start_offline() { 98 | stop 99 | if [ -n "$OFFLINE_SYSTEM_UNITS" ]; then 100 | if [ "$quiet" != true ]; then 101 | echo "Starting trusted system offline units" 102 | fi 103 | systemctl start "$OFFLINE_SYSTEM_UNITS" 104 | fi 105 | if [ -n "$OFFLINE_USER_UNITS" ]; then 106 | if [ "$quiet" != true ]; then 107 | echo "Starting trusted user offline units" 108 | fi 109 | echo "$OFFLINE_USER_UNITS" | while read -r line; do 110 | user_toggle "start" "$line" 111 | done 112 | fi 113 | } 114 | 115 | status() { 116 | echo "Systemd system units:" 117 | for unit in $TRUSTED_SYSTEM_UNITS 118 | do 119 | SYSTEMD_COLORS=1 systemctl status "$unit" | sed '1p;/^\s*Active:/!d' 120 | done 121 | echo "Systemd user units:" 122 | echo "$TRUSTED_USER_UNITS" | while read -r line; do 123 | user_toggle "status" "$line" 124 | done 125 | } 126 | 127 | while getopts ":f:sxtqh" opt; do 128 | case $opt in 129 | f) 130 | UNITFILE=$OPTARG 131 | ;; 132 | q) 133 | quiet=true 134 | ;; 135 | s) 136 | status=true 137 | ;; 138 | x) 139 | stopall=true 140 | ;; 141 | t) 142 | startall=true 143 | ;; 144 | :) 145 | echo "Option -$OPTARG requires an argument." 146 | usage 147 | exit 1 148 | ;; 149 | h | *) 150 | usage 151 | exit 152 | ;; 153 | esac 154 | done 155 | 156 | # Check if the trusted unit file exists. 157 | file_check 158 | 159 | # Get the trusted units 160 | get_trusted_units 161 | 162 | # If the status was requested, display it. 163 | if [ "$status" = true ]; then 164 | status 165 | exit $? 166 | fi 167 | 168 | # If stopping everything was requested, do it. 169 | if [ "$stopall" = true ]; then 170 | stop 171 | exit $? 172 | fi 173 | 174 | # If starting everything was requested, do it. 175 | if [ "$startall" = true ]; then 176 | start 177 | exit $? 178 | fi 179 | 180 | # Execute nmtrust. 181 | find_nmtrust 182 | $NMTRUST 183 | result=$? 184 | 185 | # Toggle the units as appropriate. 186 | if [ $result -eq 0 ]; then 187 | start 188 | exit $? 189 | elif [ $result -eq 4 ]; then 190 | start_offline 191 | exit $? 192 | else 193 | stop 194 | exit $? 195 | fi 196 | --------------------------------------------------------------------------------