Revisit WireGuard.Configuration

- Make Configuration Codable
- Expose WireGuard ConfigurationError
- Produce ConfigurationBuilder from Configuration
- Support multiple peers
- Make private key a requirement
This commit is contained in:
Davide De Rosa 2022-02-11 18:45:40 +01:00
parent c019cecbe0
commit 2bcd11fd7e
4 changed files with 111 additions and 38 deletions

View File

@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Parse OpenVPN authentication requirement from `--auth-user-pass`. - Parse OpenVPN authentication requirement from `--auth-user-pass`.
### Changed
- Support multiple peers in WireGuard.
## 4.1.0 (2022-02-09) ## 4.1.0 (2022-02-09)
### Added ### Added

View File

@ -200,13 +200,14 @@ extension WireGuard {
serverAddress: String, serverAddress: String,
serverPort: String serverPort: String
) -> WireGuardProvider.Configuration? { ) -> WireGuardProvider.Configuration? {
var builder = WireGuard.ConfigurationBuilder() var builder = WireGuard.ConfigurationBuilder(privateKey: clientPrivateKey)
builder.privateKey = clientPrivateKey
builder.addresses = [clientAddress] builder.addresses = [clientAddress]
builder.peerPublicKey = serverPublicKey
builder.peerAddress = serverAddress var peer = Peer(publicKey: serverPublicKey)
builder.peerPort = UInt16(serverPort) peer.endpoint = "\(serverAddress):\(serverPort)"
builder.allowedIPs = ["0.0.0.0/0"] peer.allowedIPs = ["0.0.0.0/0"]
builder.peers = [peer]
builder.dns = ["1.1.1.1", "1.0.0.1"] builder.dns = ["1.1.1.1", "1.0.0.1"]
guard let cfg = builder.build() else { guard let cfg = builder.build() else {
return nil return nil

View File

@ -26,9 +26,17 @@
import Foundation import Foundation
import WireGuardKit import WireGuardKit
public struct ConfigurationError: Error {
let parseError: TunnelConfiguration.ParseError
}
extension WireGuard.Configuration { extension WireGuard.Configuration {
public init(wgQuickConfig: String) throws { public init(wgQuickConfig: String) throws {
tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: wgQuickConfig) do {
tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: wgQuickConfig)
} catch let parseError as TunnelConfiguration.ParseError {
throw ConfigurationError(parseError: parseError)
}
} }
public func asWgQuickConfig() -> String { public func asWgQuickConfig() -> String {

View File

@ -25,10 +25,31 @@
import Foundation import Foundation
import WireGuardKit import WireGuardKit
import NetworkExtension
extension WireGuard { extension WireGuard {
public struct Peer {
public var publicKey: String
public var preSharedKey: String?
public var endpoint: String?
public var allowedIPs: [String]?
public var keepAliveInterval: UInt16?
public init(publicKey: String) {
self.publicKey = publicKey
}
}
public struct ConfigurationBuilder { public struct ConfigurationBuilder {
public var privateKey: String? public var privateKey: String
public var publicKey: String? {
return PrivateKey(base64Key: privateKey)?.publicKey.base64Key
}
public var addresses: [String]? public var addresses: [String]?
@ -36,31 +57,17 @@ extension WireGuard {
public var mtu: UInt16? public var mtu: UInt16?
public var peerPublicKey: String? public var peers: [Peer]
public var peerPreSharedKey: String? public init(privateKey: String) {
self.privateKey = privateKey
public var peerAddress: String? peers = []
public var peerPort: UInt16?
public var allowedIPs: [String]?
public var keepAliveInterval: UInt16?
public init() {
} }
public func build() -> Configuration? { public func build() -> Configuration? {
guard let privateKey = privateKey, let clientPrivateKey = PrivateKey(base64Key: privateKey) else { guard let clientPrivateKey = PrivateKey(base64Key: privateKey) else {
return nil return nil
} }
guard let peerPublicKey = peerPublicKey, let serverPublicKey = PublicKey(base64Key: peerPublicKey) else {
return nil
}
guard let peerAddress = peerAddress, let peerPort = peerPort, let endpoint = Endpoint(from: "\(peerAddress):\(peerPort)") else {
return nil
}
var interfaceConfiguration = InterfaceConfiguration(privateKey: clientPrivateKey) var interfaceConfiguration = InterfaceConfiguration(privateKey: clientPrivateKey)
if let clientAddresses = addresses?.mapOptional({ IPAddressRange(from: $0) }) { if let clientAddresses = addresses?.mapOptional({ IPAddressRange(from: $0) }) {
@ -70,23 +77,76 @@ extension WireGuard {
interfaceConfiguration.dns = dnsServers interfaceConfiguration.dns = dnsServers
} }
interfaceConfiguration.mtu = mtu interfaceConfiguration.mtu = mtu
var peerConfiguration = PeerConfiguration(publicKey: serverPublicKey)
if let peerPreSharedKey = peerPreSharedKey {
peerConfiguration.preSharedKey = PreSharedKey(base64Key: peerPreSharedKey)
}
if let peerAllowedIPs = allowedIPs?.mapOptional({ IPAddressRange(from: $0) }) {
peerConfiguration.allowedIPs = peerAllowedIPs
}
peerConfiguration.endpoint = endpoint
peerConfiguration.persistentKeepAlive = keepAliveInterval
let tunnelConfiguration = TunnelConfiguration(name: nil, interface: interfaceConfiguration, peers: [peerConfiguration]) var peerConfigurations: [PeerConfiguration] = []
for peer in peers {
guard let publicKey = PublicKey(base64Key: peer.publicKey) else {
continue
}
// XXX: this is actually optional in WireGuard
guard let endpointString = peer.endpoint, let endpoint = Endpoint(from: endpointString) else {
return nil
}
var cfg = PeerConfiguration(publicKey: publicKey)
if let preSharedKey = peer.preSharedKey {
cfg.preSharedKey = PreSharedKey(base64Key: preSharedKey)
}
if let allowedIPs = peer.allowedIPs?.mapOptional(IPAddressRange.init(from:)) {
cfg.allowedIPs = allowedIPs
}
cfg.endpoint = endpoint
cfg.persistentKeepAlive = peer.keepAliveInterval
peerConfigurations.append(cfg)
}
guard !peers.isEmpty else {
return nil
}
let tunnelConfiguration = TunnelConfiguration(name: nil, interface: interfaceConfiguration, peers: peerConfigurations)
return Configuration(tunnelConfiguration: tunnelConfiguration) return Configuration(tunnelConfiguration: tunnelConfiguration)
} }
} }
public struct Configuration { public struct Configuration: Codable {
public let tunnelConfiguration: TunnelConfiguration public let tunnelConfiguration: TunnelConfiguration
public init(tunnelConfiguration: TunnelConfiguration) {
self.tunnelConfiguration = tunnelConfiguration
}
public func builder() -> WireGuard.ConfigurationBuilder {
let privateKey = tunnelConfiguration.interface.privateKey.base64Key
var builder = WireGuard.ConfigurationBuilder(privateKey: privateKey)
builder.addresses = tunnelConfiguration.interface.addresses.map(\.stringRepresentation)
builder.dns = tunnelConfiguration.interface.dns.map(\.stringRepresentation)
builder.mtu = tunnelConfiguration.interface.mtu
builder.peers = tunnelConfiguration.peers.map {
var peer = Peer(publicKey: $0.publicKey.base64Key)
peer.preSharedKey = $0.preSharedKey?.base64Key
peer.endpoint = $0.endpoint?.stringRepresentation
peer.allowedIPs = $0.allowedIPs.map(\.stringRepresentation)
peer.keepAliveInterval = $0.persistentKeepAlive
return peer
}
return builder
}
// MARK: Codable
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let wg = try container.decode(String.self)
let cfg = try TunnelConfiguration(fromWgQuickConfig: wg, called: nil)
self.init(tunnelConfiguration: cfg)
}
public func encode(to encoder: Encoder) throws {
let wg = tunnelConfiguration.asWgQuickConfig()
var container = encoder.singleValueContainer()
try container.encode(wg)
}
} }
} }