@@ -21,18 +21,4 @@ states youself. | |||
# Available states | |||
## `wg.present` | |||
Creates a wireguard interface and sets interface-wide parameters. | |||
## `wg.peer_present` | |||
Adds a peer to an interface and sets peer-specific parameters. | |||
## `wg.absent` | |||
Removes a wireguard interface. | |||
## `wg.peer_absent` | |||
Removes a peer from an interface. | |||
No states. Include `wireguard` in the top.sls file. |
@@ -1,146 +0,0 @@ | |||
import yaml | |||
import os | |||
from tempfile import mkstemp | |||
__virtualname__ = 'wg' | |||
def __virtual__(): | |||
# do checks for startup | |||
return __virtualname__ | |||
def create(name): | |||
""" | |||
create a wireguard interface. This will fail if it already exists. | |||
""" | |||
ifaces = __salt__['network.interfaces']() | |||
if name not in ifaces.keys(): | |||
__salt__['cmd.run']('ip link add %s type wireguard' % (name,)) | |||
return {name: dict(new=name, old=None)} | |||
return 'Interface %s already exists' % (name,) | |||
def delete(name): | |||
""" | |||
delete a interface (not neccessarily a wireguard interface). This will fail | |||
if it does not exist. | |||
""" | |||
ifaces = __salt__['network.interfaces']() | |||
if name in ifaces.keys(): | |||
__salt__['cmd.run']('ip link del %s' % (name,)) | |||
return {name: dict(new=None, old=name)} | |||
return 'Interface %s does not exist' % (name,) | |||
def show(name=None, peer=None, hide_keys=True): | |||
if peer and not name: | |||
return 'If peer is given, name must also be given' | |||
if not name: | |||
return _wg_ifaces(hide_keys=hide_keys) | |||
elif peer: | |||
return _wg_ifaces(hide_keys=hide_keys).get(name).get('peers').get(peer) | |||
else: | |||
return _wg_ifaces(hide_keys=hide_keys).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: | |||
fd, filename = mkstemp(text=True) | |||
with open(filename, 'w') as f: | |||
f.write(private_key) | |||
os.close(fd) | |||
s = '%s private-key %s' % (s, filename) | |||
if peer: | |||
s = '%s peer %s' % (s, peer) | |||
if preshared_key != None: | |||
if not peer: | |||
return 'If preshared_key is given, peer must also be given' | |||
fd2, filename2 = mkstemp(text=True) | |||
with open(filename2, 'w') as f: | |||
f.write(preshared_key) | |||
os.close(fd2) | |||
s = '%s preshared-key %s' % (s, filename2) | |||
if endpoint: | |||
if not peer: | |||
return 'If endpoint is given, peer must also be given' | |||
s = '%s endpoint %s' % (s, endpoint) | |||
if persistent_keepalive is not None: | |||
if not peer: | |||
return 'If persistent_keepalive is given, peer must also be given' | |||
s = '%s persistent-keepalive %s' % (s, persistent_keepalive) | |||
if allowed_ips: | |||
if not peer: | |||
return 'If allowed_ips is given, peer must also be given' | |||
s = '%s allowed-ips %s' % (s, allowed_ips) | |||
r = __salt__['cmd.run'](s) | |||
if private_key: | |||
os.unlink(filename) | |||
if preshared_key: | |||
os.unlink(filename2) | |||
return r | |||
def remove_peer(name, peer): | |||
return __salt__['cmd.run']( | |||
'wg set %s peer %s remove' % (name, peer) | |||
) | |||
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(hide_keys=True): | |||
""" | |||
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 show all', | |||
env={'WG_HIDE_KEYS': 'always' if hide_keys else '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() |
@@ -1,132 +0,0 @@ | |||
__virtualname__ = 'wg' | |||
def __virtual__(): | |||
if 'wg.show' in __salt__: | |||
return __virtualname__ | |||
return False | |||
def present(name, listen_port=None, fwmark=None, private_key=None): | |||
""" | |||
Make sure a wireguard interface exists. | |||
""" | |||
ret = dict(name=name, changes=dict(), result=False, comment=None) | |||
show = __salt__['wg.show'](name, hide_keys=False) | |||
if not show: | |||
__salt__['wg.create'](name) | |||
ret['changes'][name] = 'Interface created.' | |||
show = __salt__['wg.show'](name, hide_keys=False) | |||
if listen_port and 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.' | |||
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, preshared_key=None): | |||
ret = dict(name=name, changes=dict(), result=False, comment=None) | |||
show = __salt__['wg.show'](interface, hide_keys=False) | |||
if not show: | |||
ret['comment'] = 'Interface %s does not exist.' % (interface) | |||
return ret | |||
show = __salt__['wg.show'](name=interface, peer=name, hide_keys=False) | |||
if not show: | |||
__salt__['wg.set'](interface, peer=name, endpoint=endpoint, | |||
persistent_keepalive=persistent_keepalive, | |||
allowed_ips=','.join(allowed_ips), preshared_key=preshared_key) | |||
ret['changes'][name] = 'Peer created.' | |||
ret['result'] = True | |||
return ret | |||
if endpoint and show.get('endpoint', '') != endpoint: | |||
__salt__['wg.set'](interface, peer=name, endpoint=endpoint) | |||
updated_show = __salt__['wg.show'](name=interface, peer=name) | |||
if updated_show.get('endpoint') != show.get('endpoint'): | |||
ret['changes']['endpoint'] = dict( | |||
old=show.get('endpoint'), new=updated_show.get('endpoint')) | |||
if persistent_keepalive and not show.get('persistent keepalive', '').startswith('every %s second' % (persistent_keepalive,)): | |||
__salt__['wg.set'](interface, peer=name, | |||
persistent_keepalive=persistent_keepalive) | |||
ret['changes']['persistent keepalive'] = 'persistent keepalive changed.' | |||
elif not persistent_keepalive and show.get('persistent keepalive'): | |||
__salt__['wg.set'](interface, peer=name, persistent_keepalive=0) | |||
ret['changes']['persistent keepalive'] = 'persistent keepalive removed.' | |||
if sorted(show.get('allowed ips')) != sorted(allowed_ips): | |||
__salt__['wg.set'](interface, peer=name, allowed_ips=','.join(allowed_ips)) | |||
ret['changes']['allowed ips'] = dict(new=allowed_ips, old=show.get('allowed ips')) | |||
if preshared_key and show.get('preshared key') != preshared_key: | |||
__salt__['wg.set'](interface, peer=name, preshared_key=preshared_key) | |||
ret['changes']['preshared key'] = 'preshared key changed.' | |||
elif show.get('preshared key') and not preshared_key: | |||
__salt__['wg.set'](interface, peer=name, preshared_key='') | |||
ret['changes']['preshared key'] = 'preshared key deleted.' | |||
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 |
@@ -1,30 +1,11 @@ | |||
wireguard: | |||
interfaces: | |||
wgtest: | |||
listen_port: 51820 | |||
# fwmark: 0x1 | |||
private_key: secret | |||
peers: | |||
- peer: 1ymBfBty05PNhD/QJKUlu4aL2p4jKSWVVqVQWIQG6wM= | |||
# the note: will not go into wireguard configuration | |||
# it enables you to label peers | |||
note: some_note | |||
endpoint: '10.42.0.0:1338' | |||
allowed_ips: | |||
- 10.0.0.2/32 | |||
- 'fdff::2/128' | |||
persistent_keepalive: 25 | |||
# preshared_key: secret | |||
- peer: 2ymBfBty05PNhD/QJKUlu4aL2p4jKSWVVqVQWIQG6wM= | |||
endpoint: '[2001:db8::1]:1339' | |||
allowed_ips: | |||
- 10.0.0.3/32 | |||
- 'fdff::3/128' | |||
# optionally, a list of interfaces can be specified for which forwarding will | |||
# be set to 1 via sysctl.present | |||
# ATTENTION: this option is experimental and I haven't made my mind up whether | |||
# it'll stay. Please don't rely on this for now. | |||
set_forward_interfaces: | |||
- all | |||
- wgtest | |||
wg0: | |||
config: | | |||
[Interface] | |||
Address = fe80::1/64 | |||
ListenPort = 51820 | |||
PrivateKey = private | |||
Table = off | |||
[Peer] | |||
PublicKey = peer | |||
AllowedIPs = fe80::2 |
@@ -1,45 +1,21 @@ | |||
{% from "wireguard/map.jinja" import wireguard with context %} | |||
{%- for interface in salt['pillar.get']('wireguard', {}).keys() %} | |||
wireguard_interface_{{interface}}: | |||
file.managed: | |||
- name: /etc/wireguard/{{interface}}.conf | |||
- contents_pillar: wireguard:{{interface}}:config | |||
- mode: 640 | |||
{% for interface, values in salt['pillar.get']('wireguard:interfaces', {}).items() %} | |||
wireguard_{{ interface }}: | |||
wg.present: | |||
- name: {{ interface }} | |||
{% for k, v in values.items() %} | |||
{% if k in ['listen_port', 'fwmark', 'private_key'] %} | |||
- {{k}}: {{v}} | |||
{% if salt['pillar.get']('wireguard:' ~ interface ~ ':enable', True) %} | |||
restart wg-quick@{{interface}}: | |||
service.running: | |||
- name: wg-quick@{{interface}} | |||
- enable: True | |||
- watch: | |||
- file: wireguard_interface_{{interface}} | |||
{% else %} | |||
stop and disable wg-quick@{{interface}}: | |||
service.dead: | |||
- name: wg-quick@{{interface}} | |||
- enable: False | |||
{% endif %} | |||
{% endfor %} {# values.items() #} | |||
{% for peer in values.get('peers', {}) %} | |||
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 %} | |||
{% if peer.get('preshared_key') != None %} | |||
- preshared_key: {{ peer.get('preshared_key') }} | |||
{% endif %} | |||
{% endif %} | |||
{% endfor %} | |||
{% endfor %} | |||
{% for interface in salt['pillar.get']('wireguard:set_forward_interfaces', []) %} | |||
net.ipv4.conf.{{interface}}.forwarding: | |||
sysctl.present: | |||
- value: 1 | |||
net.ipv6.conf.{{interface}}.forwarding: | |||
sysctl.present: | |||
- value: 1 | |||
{% endfor %} | |||
{%- endfor %} |