Browse Source

rewrite for simple quick and dirty use

tags/v1.0
Maximilian Eschenbacher 6 years ago
parent
commit
08872d2a83
5 changed files with 30 additions and 365 deletions
  1. +1
    -15
      README.md
  2. +0
    -146
      _modules/wireguard.py
  3. +0
    -132
      _states/wireguard.py
  4. +10
    -29
      pillar.example
  5. +19
    -43
      wireguard/init.sls

+ 1
- 15
README.md View File

@@ -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.

+ 0
- 146
_modules/wireguard.py View 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()

+ 0
- 132
_states/wireguard.py View File

@@ -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

+ 10
- 29
pillar.example View File

@@ -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

+ 19
- 43
wireguard/init.sls View File

@@ -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 %}

Loading…
Cancel
Save