aboutsummaryrefslogtreecommitdiff
path: root/templates/manage_wg_peers.sh.j2
blob: ed2f800b16dba5fae560df378c92c7f00ab184e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/bin/bash
set -e

WG_SERVER_HOME="{{ wireguard_server_home }}"
WG_PEERS_HOME="${WG_SERVER_HOME}/peers.d"
IP_FILE="${WG_SERVER_HOME}/ips.txt"
SUBNET_PREFIX="{{ wireguard_subnet_prefix }}"
DEFAULT_PORT="{{ wireguard_port }}"
DEFAULT_DNS="8.8.8.8"

test "${EUID}" -ne 0 && printf "%s\n" "run as root" && exit 1
umask 077

if ! command -v wg &>/dev/null; then
    printf "%s\n" "[err] wireguard not installed"
    exit 1
fi

function usage() {
    printf "%s\n" \
        ""                                                                      \
        "wireguard peer management script"                                      \
        "usage: $(basename ${0}) <action> [-h] <options>"                       \
        ""                                                                      \
        "add-peer           add a peer to the wg network"                       \
        "options:"                                                              \
        "-s <server>        required: wg server endpoint"                       \
        "-n <name>          optional: peer name, default random"                \
        "-p <port>          optional: wg server port, default ${DEFAULT_PORT}"  \
        "-d <dns>           optional: dns server, default ${DEFAULT_DNS}"       \
        ""                                                                      \
        "remove-peer        remove peer from the wg network"                    \
        "options:"                                                              \
        "-n <name>          required: peer name"                                \
        ""                                                                      \
        "usage              print this help message and exit"                   \
        ""                                                                      \
        "configuration files:"                                                  \
        "wg server config dir: ${WG_SERVER_HOME}"                               \
        "wg peers configs dir: ${WG_PEERS_HOME}"
    exit 1
}

function validate_ip() {
    local ip="${1}"
    if ! [[ "${ip}" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
        printf "%s\n" "[err] invalid IP address: ${ip}"
        exit 1
    fi
}

function validate_port() {
    local port="${1}"
    if ! [[ "${port}" =~ ^[0-9]+$ ]] || [[ "${port}" -lt 1 ]] || [[ "${port}" -gt 65535 ]]; then
        printf "%s\n" "[err] invalid port: ${port}"
        exit 1
    fi
}

function get_next_available_ip() {
    (
        flock -x 200
        touch "${IP_FILE}"
        for i in {2..254}; do
            ip="${SUBNET_PREFIX}.${i}"
            if ! grep -q "${ip}" "${IP_FILE}"; then
                printf "%s\n" "${ip}"
                printf "%s\n" "${ip}" >> "${IP_FILE}"
                exit 0
            fi
        done
        printf "%s\n" "[err] no available ips in range ${SUBNET_PREFIX}.2 - ${SUBNET_PREFIX}.254"
        exit 1
    ) 200>"${IP_FILE}.lock"
}

function add_peer() {
    if ! test -d "${WG_SERVER_HOME}" || ! test -f "${WG_SERVER_HOME}/wg0.conf"; then
        printf "%s\n" "[err] no wg server config found; install the server first"
        exit 1
    fi

    if ! test "${server}"; then
        printf "%s\n" "[err] missing -s <server>"
        exit 1
    fi

    mkdir -p "${WG_PEERS_HOME}/${name}" &>/dev/null
    assigned_ip=$(get_next_available_ip)
    (
        cd "${WG_PEERS_HOME}/${name}"
        wg genkey | tee "${name}.key" | wg pubkey > "${name}.pub"

        cat > "${name}.conf" << EOF
# peer ${name}
[Interface]
PrivateKey = $(cat "${name}.key")
Address    = ${assigned_ip}/24
DNS        = ${dns}

[Peer]
PublicKey           = $(wg pubkey < "${WG_SERVER_HOME}/server.key")
PresharedKey        = $(cat "${WG_SERVER_HOME}/psk.key")
Endpoint            = ${server}:${port}
AllowedIPs          = 0.0.0.0/0
PersistentKeepalive = 25
EOF
        printf "%s\n" \
            "[inf] generated peer configuration ${name} to server ${server}" \
            "[inf] config: ${WG_PEERS_HOME}/${name}/${name}.conf"
    )

    (
        peer_public_key=$(wg pubkey < "${WG_PEERS_HOME}/${name}/${name}.key")
        cat >> "${WG_SERVER_HOME}/wg0.conf" << EOF

# peer ${name}
[Peer]
PublicKey    = ${peer_public_key}
PresharedKey = $(cat "${WG_SERVER_HOME}/psk.key")
AllowedIPs   = ${assigned_ip}/32
# peer ${name}
EOF
        systemctl restart wg-quick@wg0.service
        printf "%s\n" "[inf] restarted wg-quick@wg0.service"
    )
}

function remove_peer() {
    if ! test "${name}"; then
        printf "%s\n" "[err] missing -n <name>"
        exit 1
    fi

    if grep -qi "^# peer ${name}" "${WG_SERVER_HOME}/wg0.conf"; then
        sed -i "/# peer ${name}/,/# peer ${name}/ s/^/# /" "${WG_SERVER_HOME}/wg0.conf"
        systemctl restart wg-quick@wg0.service
        printf "%s\n" "[inf] removed peer ${name} and restarted wg server"
    else
        printf "%s\n" "[err] no such peer ${name} in ${WG_SERVER_HOME}/wg0.conf"
        exit 1
    fi
}

port="${DEFAULT_PORT}"
dns="${DEFAULT_DNS}"

if test "${1}"; then
    action="${1}"
    shift
    case "${action}" in
        add-peer)
            while getopts "s:n:p:d:h" opts; do
                case "${opts}" in
                    s) server="${OPTARG}"; validate_ip "${server}";;
                    n) name="${OPTARG}";;
                    p) port="${OPTARG}"; validate_port "${port}";;
                    d) dns="${OPTARG}"; validate_ip "${dns}";;
                    h) usage;;
                    *) usage;;
                esac
            done
            rand=$(printf "%s\n" "${RANDOM}" | md5sum | fold -w4 | head -1)
            name="${name:-${rand}}"
            add_peer
            ;;
        remove-peer)
            while getopts "n:h" opts; do
                case "${opts}" in
                    n) name="${OPTARG}";;
                    h) usage;;
                    *) usage;;
                esac
            done
            remove_peer
            ;;
        usage)
            usage
            ;;
        *)
            usage
            ;;
    esac
else
    usage
fi