Skip to content

Commit

Permalink
F #68: Add VR/WG support
Browse files Browse the repository at this point in the history
- Add onewg VPN admin tool
- Add Service::WireGuard feature to VR
- Adjust Service::Failover accordingly
- Add basic JSON schema validation
  • Loading branch information
sk4zuzu committed Mar 29, 2024
1 parent feadcca commit 2c6838f
Show file tree
Hide file tree
Showing 4 changed files with 588 additions and 1 deletion.
3 changes: 3 additions & 0 deletions appliances/VRouter/Failover/execute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ module Failover
fallback: 'NO' },

'one-dhcp4' => { _ENABLED: 'ONEAPP_VNF_DHCP4_ENABLED',
fallback: 'NO' },

'one-wg' => { _ENABLED: 'ONEAPP_VNF_WG_ENABLED',
fallback: 'NO' }
}

Expand Down
2 changes: 1 addition & 1 deletion appliances/VRouter/NAT4/main.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module NAT4

ONEAPP_VNF_NAT4_ENABLED = env :ONEAPP_VNF_NAT4_ENABLED, 'NO'

ONEAPP_VNF_NAT4_INTERFACES_OUT = env :ONEAPP_VNF_NAT4_INTERFACES_OUT, '' # nil -> none, empty -> all
ONEAPP_VNF_NAT4_INTERFACES_OUT = env :ONEAPP_VNF_NAT4_INTERFACES_OUT, nil # nil -> none, empty -> all

def parse_env
@interfaces_out ||= parse_interfaces ONEAPP_VNF_NAT4_INTERFACES_OUT
Expand Down
152 changes: 152 additions & 0 deletions appliances/VRouter/WireGuard/main.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# frozen_string_literal: true

require 'erb'
require 'ipaddr'
require 'yaml'
require_relative '../vrouter.rb'

begin
require 'json-schema'
rescue LoadError
# NOTE: This handles the install stage.
end

module Service
module WireGuard
extend self

DEPENDS_ON = %w[Service::Failover]

ONEAPP_VNF_WG_ENABLED = env :ONEAPP_VNF_WG_ENABLED, 'NO'

ONEAPP_VNF_WG_INTERFACES_OUT = env :ONEAPP_VNF_WG_INTERFACES_OUT, nil # nil -> none, empty -> all

ONEAPP_VNF_WG_CFG_LOCATION = env :ONEAPP_VNF_WG_CFG_LOCATION, '/dev/sr0:/onewg.yml'

def parse_env
@interfaces_out ||= parse_interfaces ONEAPP_VNF_WG_INTERFACES_OUT
@mgmt ||= detect_mgmt_nics
@interfaces ||= @interfaces_out.keys - @mgmt

iso_path, cfg_path = ONEAPP_VNF_WG_CFG_LOCATION.split(%[:])

schema = YAML.load bash("#{File.dirname(__FILE__)}/onewg schema show", chomp: true)

document = YAML.load bash("isoinfo -i #{iso_path} -R -x #{cfg_path}", chomp: true)

if JSON::Validator.validate(schema, document)
{ cfg: document }
else
msg :error, 'YAML config looks invalid!'
{ cfg: nil }
end
end

def install(initdir: '/etc/init.d')
msg :info, 'WireGuard::install'

puts bash 'apk --no-cache add cdrkit ruby wireguard-tools-wg-quick'
puts bash 'gem install --no-document json-schema'

file "#{initdir}/one-wg", <<~SERVICE, mode: 'u=rwx,g=rx,o='
#!/sbin/openrc-run
source /run/one-context/one_env
command="/usr/bin/ruby"
command_args="-r /etc/one-appliance/lib/helpers.rb -r #{__FILE__}"
depend() {
after net firewall keepalived
}
start() {
$command $command_args -e Service::WireGuard.execute 1>>/var/log/one-appliance/one-wg.log 2>&1
}
stop() {
$command $command_args -e Service::WireGuard.cleanup 1>>/var/log/one-appliance/one-wg.log 2>&1
}
SERVICE

toggle [:update]
end

def configure(basedir: '/etc/wireguard')
msg :info, 'WireGuard::configure'

unless ONEAPP_VNF_WG_ENABLED
# NOTE: We always disable it at re-contexting / reboot in case an user enables it manually.
toggle [:stop, :disable]
return
end

parse_env[:cfg]&.each do |dev, opts|
unless @interfaces.include?(opts['interface_out'])
msg :error, "Forbidden outgoing interface: #{opts['interface_out']}"
next
end

subnet = IPAddr.new(opts['peer_subnet'])

peers = opts['peers'].to_h.each_with_object({}) do |(k, v), acc|
next if v['public_key'].nil? && v['private_key'].nil?

v['public_key'] ||= bash("wg pubkey <<< #{v['private_key']}", chomp: true)

acc[k] = v
end

file "#{basedir}/#{dev}.conf", ERB.new(<<~PEER, trim_mode: '-').result(binding), mode: 'u=rw,g=r,o=', overwrite: true
[Interface]
Address = <%= subnet.succ.to_s %>/<%= subnet.prefix %>
ListenPort = <%= opts['server_port'] %>
PrivateKey = <%= opts['private_key'] %>
<%- peers.each do |k, v| -%>
[Peer]
PresharedKey = <%= v['preshared_key'] %>
PublicKey = <%= v['public_key'] %>
AllowedIPs = <%= v['address'].split(%[/])[0] %>/32
<%- end -%>
PEER
end
end

def execute
msg :info, 'WireGuard::execute'

parse_env[:cfg]&.each do |dev, _|
bash <<~BASH
wg-quick up '#{dev}'
echo 1 > '/proc/sys/net/ipv4/conf/#{dev}/forwarding'
BASH
end
end

def cleanup
msg :info, 'WireGuard::cleanup'

parse_env[:cfg]&.each do |dev, _|
bash "wg-quick down '#{dev}'"
end
end

def toggle(operations)
operations.each do |op|
msg :info, "WireGuard::toggle([:#{op}])"
case op
when :disable
puts bash 'rc-update del one-wg default ||:'
when :update
puts bash 'rc-update -u'
else
puts bash "rc-service one-wg #{op.to_s}"
end
end
end

def bootstrap
msg :info, 'WireGuard::bootstrap'
end
end
end
Loading

0 comments on commit 2c6838f

Please sign in to comment.