@@ -0,0 +1,27 @@ | |||
# 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 | |||
``` |
@@ -0,0 +1,120 @@ | |||
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() |
@@ -0,0 +1,120 @@ | |||
__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 |
@@ -0,0 +1,22 @@ | |||
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' |
@@ -0,0 +1,5 @@ | |||
# -*- coding: utf-8 -*- | |||
# vim: ft=yaml | |||
wireguard: | |||
package: wireguard |
@@ -0,0 +1,35 @@ | |||
{% 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 %} |
@@ -0,0 +1,15 @@ | |||
# -*- 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') | |||
%} |
@@ -0,0 +1,2 @@ | |||
Debian: | |||
foo: bar |