Initial commit (Security Hardened Slate)

This commit is contained in:
esefxdz committed 2026-05-29 14:42:24 +03:00
commit 157f60aad2
65 files changed
+3735

No files matched your search

@@ -0,0 +1,117 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';
import 'package:process_run/shell.dart';
import '../wireguard_flutter_platform_interface.dart';
class WireGuardFlutterLinux extends WireGuardFlutterInterface {
String? name;
File? configFile;
VpnStage _stage = VpnStage.noConnection;
final _stageController = StreamController<VpnStage>.broadcast();
void _setStage(VpnStage stage) {
_stage = stage;
_stageController.add(stage);
}
final shell = Shell(runInShell: true, verbose: kDebugMode);
@override
Future<void> initialize({required String interfaceName}) async {
name = interfaceName.replaceAll(' ', '_');
await refreshStage();
}
Future<String> get filePath async {
final tempDir = await getTemporaryDirectory();
return '${tempDir.path}${Platform.pathSeparator}$name.conf';
}
@override
Future<void> startVpn({
required String serverAddress,
required String wgQuickConfig,
required String providerBundleIdentifier,
}) async {
final isAlreadyConnected = await isConnected();
if (!isAlreadyConnected) {
_setStage(VpnStage.preparing);
} else {
debugPrint('Already connected');
}
try {
configFile = await File(await filePath).create();
await configFile!.writeAsString(wgQuickConfig);
} on PathAccessException {
debugPrint('Denied to write file. Trying to start interface');
if (isAlreadyConnected) {
return _setStage(VpnStage.connected);
}
try {
await shell.run('sudo wg-quick up $name');
} catch (_) {
} finally {
_setStage(VpnStage.denied);
}
}
if (!isAlreadyConnected) {
_setStage(VpnStage.connecting);
await shell.run('sudo wg-quick up ${configFile?.path ?? await filePath}');
_setStage(VpnStage.connected);
}
}
@override
Future<void> stopVpn() async {
assert(
(await isConnected()),
'Bad state: vpn has not been started. Call startVpn',
);
_setStage(VpnStage.disconnecting);
try {
await shell
.run('sudo wg-quick down ${configFile?.path ?? (await filePath)}');
} catch (e) {
await refreshStage();
rethrow;
}
await refreshStage();
}
@override
Future<VpnStage> stage() async => _stage;
@override
Stream<VpnStage> get vpnStageSnapshot => _stageController.stream;
@override
Future<void> refreshStage() async {
if (await isConnected()) {
_setStage(VpnStage.connected);
} else if (name == null) {
_setStage(VpnStage.waitingConnection);
} else if (configFile == null) {
_setStage(VpnStage.noConnection);
} else {
_setStage(VpnStage.disconnected);
}
}
@override
Future<bool> isConnected() async {
assert(
name != null,
'Bad state: not initialized. Call "initialize" before calling this command',
);
final processResultList = await shell.run('sudo wg');
final process = processResultList.first;
return process.outLines.any((line) => line.trim() == 'interface: $name');
}
}
@@ -0,0 +1,62 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:wireguard_flutter/linux/wireguard_flutter_linux.dart';
import 'package:wireguard_flutter/wireguard_flutter_method_channel.dart';
import 'wireguard_flutter_platform_interface.dart';
export 'wireguard_flutter_platform_interface.dart' show VpnStage;
class WireGuardFlutter extends WireGuardFlutterInterface {
static WireGuardFlutterInterface? __instance;
static WireGuardFlutterInterface get _instance => __instance!;
static WireGuardFlutterInterface get instance {
registerWith();
return _instance;
}
static void registerWith() {
if (__instance == null) {
if (kIsWeb) {
throw UnsupportedError('The web platform is not supported');
} else if (Platform.isLinux) {
__instance = WireGuardFlutterLinux();
} else {
__instance = WireGuardFlutterMethodChannel();
}
}
}
WireGuardFlutter._();
@override
Stream<VpnStage> get vpnStageSnapshot => _instance.vpnStageSnapshot;
@override
Future<void> initialize({required String interfaceName}) {
return _instance.initialize(interfaceName: interfaceName);
}
@override
Future<void> startVpn({
required String serverAddress,
required String wgQuickConfig,
required String providerBundleIdentifier,
}) async {
return _instance.startVpn(
serverAddress: serverAddress,
wgQuickConfig: wgQuickConfig,
providerBundleIdentifier: providerBundleIdentifier,
);
}
@override
Future<void> stopVpn() => _instance.stopVpn();
@override
Future<void> refreshStage() => _instance.refreshStage();
@override
Future<VpnStage> stage() => _instance.stage();
}
@@ -0,0 +1,60 @@
import 'package:flutter/services.dart';
import 'wireguard_flutter_platform_interface.dart';
class WireGuardFlutterMethodChannel extends WireGuardFlutterInterface {
static const _methodChannelVpnControl =
"billion.group.wireguard_flutter/wgcontrol";
static const _methodChannel = MethodChannel(_methodChannelVpnControl);
static const _eventChannelVpnStage =
'billion.group.wireguard_flutter/wgstage';
static const _eventChannel = EventChannel(_eventChannelVpnStage);
@override
Stream<VpnStage> get vpnStageSnapshot =>
_eventChannel.receiveBroadcastStream().map(
(event) => event == VpnStage.denied.code
? VpnStage.disconnected
: VpnStage.values.firstWhere(
(stage) => stage.code == event,
orElse: () => VpnStage.noConnection,
),
);
@override
Future<void> initialize({required String interfaceName}) {
return _methodChannel.invokeMethod("initialize", {
"localizedDescription": interfaceName,
"win32ServiceName": interfaceName,
});
}
@override
Future<void> startVpn({
required String serverAddress,
required String wgQuickConfig,
required String providerBundleIdentifier,
}) async {
return _methodChannel.invokeMethod("start", {
"serverAddress": serverAddress,
"wgQuickConfig": wgQuickConfig,
"providerBundleIdentifier": providerBundleIdentifier,
});
}
@override
Future<void> stopVpn() => _methodChannel.invokeMethod('stop');
@override
Future<void> refreshStage() => _methodChannel.invokeMethod("refresh");
@override
Future<VpnStage> stage() => _methodChannel.invokeMethod("stage").then(
(value) => value != null
? VpnStage.values.firstWhere(
(stage) => stage.code == value.toString(),
orElse: () => VpnStage.disconnected,
)
: VpnStage.disconnected,
);
}
@@ -0,0 +1,36 @@
abstract class WireGuardFlutterInterface {
Stream<VpnStage> get vpnStageSnapshot;
Future<void> initialize({required String interfaceName});
Future<void> startVpn({
required String serverAddress,
required String wgQuickConfig,
required String providerBundleIdentifier,
});
Future<void> stopVpn();
Future<void> refreshStage();
Future<VpnStage> stage();
Future<bool> isConnected() =>
stage().then((stage) => stage == VpnStage.connected);
}
enum VpnStage {
connected('connected'),
connecting('connecting'),
disconnecting('disconnecting'),
disconnected('disconnected'),
waitingConnection('wait_connection'),
authenticating('authenticating'),
reconnect('reconnect'),
noConnection('no_connection'),
preparing('prepare'),
denied('denied'),
exiting('exiting');
final String code;
const VpnStage(this.code);
}