# wireguard-formula | |||||
currently with only `_modules` and `_states`. | |||||
# Install | |||||
Add the path to the wireguard-formula to the master configuration file under option | |||||
`file_roots` or as `gitfs_remotes`. | |||||
# Use as module | |||||
`salt-call wg.create wgtest` | |||||
`salt-call wg.show wgtest` | |||||
`salt-call wg.set interface listen_port=1337` | |||||
`salt-call wg.delete wgtest` | |||||
# Use as state | |||||
``` | |||||
wgtest: | |||||
wg.present: | |||||
- listen_port: 1337 | |||||
1ymBfBty05PNhD/QJKUlu4aL2p4jKSWVVqVQWIQG6wM=: | |||||
wg.peer_present: | |||||
- interface: wgtest | |||||
``` |
import yaml | |||||
import os | |||||
__virtualname__ = 'wg' | |||||
def __virtual__(): | |||||
# do checks for startup | |||||
return __virtualname__ | |||||
def create(name): | |||||
""" | |||||
create a wireguard interface. This will fail if it already exists. | |||||
""" | |||||
__salt__['cmd.run']('ip link add %s type wireguard' % (name,)) | |||||
return show(name) | |||||
def delete(name): | |||||
""" | |||||
delete a wireguard interface. This will fail if it does not exist. | |||||
""" | |||||
return __salt__['cmd.run']('ip link del %s type wireguard' % (name,)) | |||||
def show(name=None, peer=None): | |||||
if peer and not name: | |||||
return 'If peer is given, name must also be given' | |||||
if not name: | |||||
return _wg_ifaces() | |||||
elif peer: | |||||
return _wg_ifaces().get(name).get('peers').get(peer) | |||||
else: | |||||
return _wg_ifaces().get(name) | |||||
def showconf(name): | |||||
return __salt__['cmd.run']('wg showconf %s' % (name,)) | |||||
def set(name, listen_port=None, fwmark=None, private_key=None, peer=None, | |||||
preshared_key=None, endpoint=None, persistent_keepalive=None, | |||||
allowed_ips=None, remove=False): | |||||
s = 'wg set %s' % (name,) | |||||
if remove: | |||||
if not peer: | |||||
return 'If remove is given, peer must also be given' | |||||
return __salt__['cmd.run']( | |||||
'%s peer %s remove' % (s, peer) | |||||
) | |||||
if listen_port: | |||||
s = '%s listen-port %s' % (s, listen_port) | |||||
if fwmark: | |||||
s = '%s fwmark %s' % (s, fwmark) | |||||
if private_key: | |||||
assert os.stat(private_key) | |||||
# TODO private key must be given as file | |||||
s = '%s private-key %s' % (s, private_key) | |||||
if peer: | |||||
s = '%s peer %s' % (s, peer) | |||||
if preshared_key: | |||||
s = '%s preshared-key %s' % (s, preshared_key) | |||||
if endpoint: | |||||
s = '%s endpoint %s' % (s, endpoint) | |||||
if persistent_keepalive: | |||||
s = '%s persistent-keepalive %s' % (s, persistent_keepalive) | |||||
if allowed_ips: | |||||
s = '%s allowed-ips %s' % (s, allowed_ips) | |||||
return __salt__['cmd.run'](s) | |||||
def remove_peer(name, peer): | |||||
return __salt__['cmd.run']( | |||||
'wg set %s peer %s remove' % (name, peer) | |||||
) | |||||
# def add_peer(name, public_key, allowed_ips=None): | |||||
# base = 'wg set %s peer %s' % (name, peer) | |||||
# | |||||
# return __salt__['cmd.run']( | |||||
# ) | |||||
def genkey(): | |||||
return __salt__['cmd.run']('wg genkey') | |||||
def genpsk(): | |||||
return __salt__['cmd.run']('wg genpsk') | |||||
def setconf(name, path): | |||||
return __salt__['cmd.run']('wg setconf %s %s' % (name, path)) | |||||
def addconf(name, path): | |||||
return __salt__['cmd.run']('wg addconf %s %s' % (name, path)) | |||||
def _wg_ifaces(): | |||||
""" | |||||
Parse output from 'wg show' | |||||
""" | |||||
# from https://github.com/saltstack/salt/blob/develop/salt/modules/linux_ip.py | |||||
tmp = dict() | |||||
tmpiface = dict() | |||||
ifaces = dict() | |||||
out = __salt__['cmd.run']('wg', env={'WG_HIDE_KEYS': 'never'}) | |||||
for line in out.splitlines(): | |||||
if line.startswith('interface: '): | |||||
k, v = _wg_splitline(line) | |||||
ifaces[v] = dict(peers=dict()) | |||||
tmpiface = ifaces[v] | |||||
tmp = tmpiface | |||||
elif line.startswith('peer: '): | |||||
k, v = _wg_splitline(line) | |||||
tmpiface['peers'][v] = dict() | |||||
tmp = tmpiface['peers'][v] | |||||
elif line == '': | |||||
continue | |||||
k, v = _wg_splitline(line) | |||||
if k == 'allowed ips': | |||||
tmp[k] = [ s.strip() for s in v.split(',') ] | |||||
else: | |||||
tmp[k] = v | |||||
return ifaces | |||||
def _wg_splitline(line): | |||||
parts = line.split(':', 1) | |||||
return parts[0].strip(), parts[1].strip() |
__virtualname__ = 'wg' | |||||
def __virtual__(): | |||||
if 'wg.show' in __salt__: | |||||
return __virtualname__ | |||||
return False | |||||
def present(name, listen_port=None, fwmark=None, private_key=None, | |||||
preshared_key=None): | |||||
""" | |||||
Make sure a wireguard interface exists. | |||||
""" | |||||
ret = dict(name=name, changes=dict(), result=False, comment=None) | |||||
interface = __salt__['wg.show'](name) | |||||
if not interface: | |||||
interface = __salt__['wg.create'](name) | |||||
ret['changes'][name] = 'Interface created.' | |||||
show = __salt__['wg.show'](name) | |||||
if int(show.get('listening port', 0)) != int(listen_port): | |||||
__salt__['wg.set'](name, listen_port=listen_port) | |||||
ret['changes']['listening port'] = dict( | |||||
old=show.get('listening port', 0), | |||||
new=listen_port, | |||||
) | |||||
if show.get('fwmark', None) != fwmark: | |||||
__salt__['wg.set'](name, fwmark=fwmark) | |||||
ret['changes']['fwmark'] = dict( | |||||
old=show.get('fwmark', None), | |||||
new=fwmark, | |||||
) | |||||
if show.get('private key') != private_key: | |||||
__salt__['wg.set'](name, private_key=private_key) | |||||
ret['changes']['private key'] = 'private key changed.' | |||||
if show.get('preshared key') != preshared_key: | |||||
__salt__['wg.set'](name, preshared_key=preshared_key) | |||||
ret['changes']['preshared key'] = 'preshared key changed.' | |||||
ret['result'] = True | |||||
return ret | |||||
def absent(name): | |||||
""" | |||||
Make sure a wireguard interface is absent. | |||||
""" | |||||
ret = dict(name=name, changes=dict(), result=False, comment=None) | |||||
interface = __salt__['wg.show'](name) | |||||
if not interface: | |||||
ret['comment'] = 'Interface %s already absent.' % (name,) | |||||
ret['result'] = True | |||||
return ret | |||||
__salt__['wg.delete'](name) | |||||
ret['changes'][name] = dict(old=name, new=None) | |||||
ret['result'] = True | |||||
return ret | |||||
def peer_present(name, interface, endpoint=None, persistent_keepalive=None, | |||||
allowed_ips=None): | |||||
ret = dict(name=name, changes=dict(), result=False, comment=None) | |||||
show = __salt__['wg.show'](interface) | |||||
if not show: | |||||
ret['comment'] = 'Interface %s does not exist.' % (interface) | |||||
return ret | |||||
show = __salt__['wg.show'](name=interface, peer=name) | |||||
if not show: | |||||
__salt__['wg.set'](interface, peer=name, endpoint=endpoint, | |||||
persistent_keepalive=persistent_keepalive, | |||||
allowed_ips=','.join(allowed_ips)) | |||||
ret['changes'][name] = 'Peer created.' | |||||
ret['result'] = True | |||||
return ret | |||||
if show.get('endpoint') != endpoint: | |||||
__salt__['wg.set'](interface, peer=name, endpoint=endpoint) | |||||
ret['changes']['endpoint'] = 'Endpoint changed.' | |||||
if show.get('persistent keepalive', None) != persistent_keepalive: | |||||
__salt__['wg.set'](interface, peer=name, | |||||
persistent_keepalive=persistent_keepalive) | |||||
ret['changes']['persistent keepalive'] = 'persistent keepalive changed.' | |||||
ret['result'] = True | |||||
return ret | |||||
def peer_absent(name, interface): | |||||
ret = dict(name=name, changes=dict(), result=False, comment=None) | |||||
show = __salt__['wg.show'](interface) | |||||
if not show: | |||||
ret['comment'] = 'Interface %s does not exist.' % (interface) | |||||
return ret | |||||
show = __salt__['wg.show'](name=interface, peer=name) | |||||
if not show: | |||||
ret['comment'] = 'Peer %s already absent.' % (name) | |||||
ret['result'] = True | |||||
return ret | |||||
__salt__['wg.set'](interface, peer=name, remove=True) | |||||
ret['changes'][name] = dict(old=name, new=None) | |||||
ret['result'] = True | |||||
return ret |
wireguard: | |||||
interfaces: | |||||
wgtest: | |||||
listen_port: 1337 | |||||
fwmark: 0x1 | |||||
private_key: secret | |||||
preshared_key: secret | |||||
peers: | |||||
wgtest: | |||||
- | |||||
peer: 1ymBfBty05PNhD/QJKUlu4aL2p4jKSWVVqVQWIQG6wM= | |||||
endpoint: '127.0.0.1:1338' | |||||
allowed_ips: | |||||
- 10.0.0.2/32 | |||||
- 'fdff::2/128' | |||||
persistent_keepalive: 25 | |||||
- | |||||
peer: 2ymBfBty05PNhD/QJKUlu4aL2p4jKSWVVqVQWIQG6wM= | |||||
endpoint: '127.0.0.1:1339' | |||||
allowed_ips: | |||||
- 10.0.0.3/32 | |||||
- 'fdff::3/128' |
# -*- coding: utf-8 -*- | |||||
# vim: ft=yaml | |||||
wireguard: | |||||
package: wireguard |
{% from "wireguard/map.jinja" import wireguard with context %} | |||||
wireguard: | |||||
pkg.installed: | |||||
- name: {{ wireguard.package }} | |||||
{% for name, values in salt['pillar.get']('wireguard:interfaces', {}).items() %} | |||||
wireguard_{{ name }}: | |||||
wg.present: | |||||
- name: {{ name }} | |||||
{% for k, v in values.items() %} | |||||
- {{k}}: {{v}} | |||||
{% endfor %} | |||||
{% endfor %} | |||||
{% for interface, peerlist in salt['pillar.get']('wireguard:peers', {}).items() %} | |||||
{% for peer in peerlist %} | |||||
wireguard_{{ interface }}_peer_{{ peer.get('peer') }}: | |||||
wg.peer_present: | |||||
- interface: {{ interface }} | |||||
- name: {{ peer.get('peer') }} | |||||
{% if peer.get('endpoint') != None %} | |||||
- endpoint: {{ peer.get('endpoint') }} | |||||
{% endif %} | |||||
{% if peer.get('persistent_keepalive') != None %} | |||||
- persistent_keepalive: {{ peer.get('persistent_keepalive') }} | |||||
{% endif %} | |||||
{% if peer.get('allowed_ips') != None %} | |||||
- allowed_ips: | |||||
{% for subnet in peer.get('allowed_ips', []) %} | |||||
- {{subnet}} | |||||
{% endfor %} | |||||
{% endif %} | |||||
{% endfor %} | |||||
{% endfor %} |
# -*- coding: utf-8 -*- | |||||
# vim: ft=jinja | |||||
{% import_yaml "wireguard/defaults.yaml" as defaults %} | |||||
{% import_yaml "wireguard/osmap.yaml" as osmap %} | |||||
{% set wireguard = salt['grains.filter_by']( | |||||
defaults, | |||||
merge=salt['grains.filter_by']( | |||||
osmap, | |||||
grain='os', | |||||
merge=salt['pillar.get']('wireguard:lookup', {}), | |||||
), | |||||
base='wireguard') | |||||
%} |
Debian: | |||||
foo: bar |