2 # Copyright (C) 2017 Ian Kelling
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
17 [[ $EUID == 0 ]] ||
exec sudo
-E "$BASH_SOURCE" "$@"
19 if [[ ! $ERRHANDLE_PATH ]]; then
20 ERRHANDLE_PATH
=$
(readlink
-f "${BASH_SOURCE}")
21 ERRHANDLE_PATH
=$
(readlink
-f ${ERRHANDLE_PATH%/*}/..
/errhandle
)
24 for p
in $ERRHANDLE_PATH/{errcatch-function
,bash-trace-function
}; do
35 trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
40 usage: ${0##*/} [OPTS] start|stop NS_NAME
41 Setup new or systemd created network namespace with nat and mount namespace
43 -c, --create Create a named network namespace. When running from
44 the same network namespace as pid 1, this is set automatically.
45 This is the case when running outside a systemd created
47 -h, --help Show this help and exit.
49 From within a systemd network namespace, nat it to the outside. This
50 would be called from ExecStartPre, and or subsequent units called with
51 JoinsNamespaceOf= and PrivateNetwork=true.
53 If given -c, or if in the default network namespace, create a named
54 network namepace natted to the current netns.
56 Uses /24 network, finding the first locally unused one starting at
59 Also create a named mount namespace under /root/mount_namespaces, so we
60 can alter some system config for this namespace. Subsequent systemd
61 command lines would be prefixed with:
63 /usr/bin/nsenter --mount=/root/mount_namespaces/NS_NAME
65 Note, this means that they can't run as unpriveledged users, but once
66 systemd 233 comes out, it will have a bind mount option from within unit
67 files, so the mount namespace won't be needed for most use cases, and I
68 will update the script to that the mount namespace not created unless a
69 flag is passed in. Patch welcome to add that flag before then.
71 A recommmended dependency of this script is my other repo named "errhandle",
72 which prints stack trace on error, and calls a cleanup function:
73 https://iankelling.org/git/?p=errhandle, set ERRHANDLE_PATH, or put it
74 in a directory adjacent to the absolute, resolved directory this file is
79 This script does not make the namespace be named like ip does, because
80 the naming is not necessary, although it could have been done with some
81 more work. For debugging and joining the namespace with a bash shell, I
82 use nsenter -n -m -t $(pgrep PROCESS_IN_NAMESPACE). Note: if I knew how
83 to easily ask systemd what pid a unit has, i would do that.
85 "ip netns new ..." also does a mount namespace, then bind
86 mounts each file/dir in /etc/netns/NS_NAME to /etc/NS_NAME. Note,
87 for openvpn having it's own resolv.conf by using it's user script which
88 calls resolvconf, this doesn't help much. What we actually want to do is
89 copy /run/resolvconf somehwere then bind mount it on top of
92 Please email me if you have a patches, bugs, feedback, or republish this
93 somewhere else: Ian Kelling <ian@iankelling.org>.
99 #### begin arg parsing ####
101 temp
=$
(getopt
-l help,create hc
"$@") || usage
1
105 -c|
--create) create
=true
; shift ;;
108 *) echo "$0: Internal error!" ; exit 1 ;;
111 if (( $# != 2 )); then
116 nn
=$2 # namespace name
117 #### end arg parsing ####
119 #### begin sanity checking ####
121 if ! type -p ip
&>/dev
/null
; then
122 echo "please install the iproute2 package"
125 if ! type -p iptables
&>/dev
/null
; then
126 echo "please install the iptables package"
129 if $install_error; then
132 #### end sanity checking ####
139 if ! $create && [[ $
(readlink
/proc
/self
/ns
/net
) == "$(readlink /proc/1/ns/net)" ]]; then
143 # make the default network namespace be named
144 target
=/run
/netns
/default
145 if [[ ! -e $target && ! -L $target ]]; then
147 ln -s /proc
/1/ns
/net
$target
151 ipd
() { ip
-n default
"$@"; }
153 ipnn
() { ip
-n $nn "$@"; }
155 # we are already in the network namespace and it's unnamed.
158 dexec
() { ip netns
exec default
"$@"; }
161 # background: head -n1 is defensive. Not sure if there is some weird feature
162 # for 2 routes to be 0/0.
163 gateway_if
=$
(ipd route list exact
0/0 |
head -n1|
sed -r 's/.*\s(\S+)\s*$/\1/')
164 nat
() { dexec iptables
-t nat
$1 POSTROUTING
-o $gateway_if -j MASQUERADE \
165 -m comment
--comment "systemd network namespace nat"; }
170 ips
="$(ipd addr show | awk '$1 == "inet
" {print $2}')"
171 for ((i
=0; i
<= 254; i
++)); do
173 if printf "%s\n" "$ips" |
grep "^${network//./\\.}" >/dev
/null
; then
185 echo "$0: error: no open network found"
189 #### begin mount namespace setup ####
190 mkdir
-p /root
/mount_namespaces
191 if ! mountpoint
/root
/mount_namespaces
>/dev
/null
; then
192 mount
--bind /root
/mount_namespaces
/root
/mount_namespaces
193 mount
--make-private /root
/mount_namespaces
195 if [[ ! -e /root
/mount_namespaces
/$nn ]]; then
196 touch /root
/mount_namespaces
/$nn
198 if ! mountpoint
/root
/mount_namespaces
/$nn >/dev
/null
; then
199 unshare
--mount=/root
/mount_namespaces
/$nn
201 #### end mount namespace setup ####
206 ip
-n $nn link
set dev lo up
209 echo 1 | dexec
dd of
=/proc
/sys
/net
/ipv
4/ip_forward
2>/dev
/null
211 _errcatch_cleanup
=stop
212 ipnn link add
$v0 type veth peer name
$v1
213 ipnn link
set $v0 netns default
214 ipd addr add
$network.1/24 dev
$v0
216 nat
-C &>/dev
/null || nat
-A
217 ipnn addr add
$network.2/24 dev
$v1
219 ipnn route add default via
$network.1
224 if ipd link list
$v0 &>/dev
/null
; then
225 # this also deletes $v1 and the route we added.
230 if nat
-C &>/dev
/null
; then nat
-D; fi
235 if mountpoint
/root
/mount_namespaces
/$nn >/dev
/null
; then
236 umount
/root
/mount_namespaces
/$nn
245 echo "$0: error: unsupported action"