コード例 #1
0
        public IEnumerable <IServiceConfig> Generate(Type type)
        {
            var processors = new Dictionary <Type, Func <IEnumerable <IServiceConfig> > >
            {
                {
                    typeof(HTTPProxy), delegate
                    {
                        var storedConfig  = _db.Single <Configuration>(x => x.Key == ConfigKeys.HttpConfig);
                        var serviceConfig = JsonConvert.DeserializeObject <HTTPConfig>(storedConfig.Value);


                        if (serviceConfig == null)
                        {
                            _logger.LogError("TG: No listeners could be retrieved from the DB for " +
                                             typeof(HTTPProxy) + ", using defaults.");
                            serviceConfig = Defaults.HTTP.Value;
                        }

                        return(new List <IServiceConfig> {
                            serviceConfig
                        });
                    }
                },
                {
                    typeof(OpenVPN), delegate
                    {
                        // In scope OpenVPN Configuration List.
                        var configs = new List <OpenVPNConfig>();

                        // Select attributes from local database.
                        var storedOpenVPNConfig = _db.Select <Configuration>(x => x.Key.Contains("vpn.openvpn."));
                        var storedCryptoConfig  = _db.Select <Configuration>(x => x.Key.Contains("crypto."));

                        // Check if values exist.
                        if (storedOpenVPNConfig == null || storedCryptoConfig == null)
                        {
                            _logger.LogError("TG: Could not fetch OpenVPN or Crypto config from the database. Please re-install, no defaults are possible for the CA/PKI.");
                            return(null);
                        }

                        // Certificate Information Placeholder Object.
                        var certInfo = new CertificateBaseInformation();

                        // Iterate through each configuration key.
                        foreach (var cryptoConfig in storedCryptoConfig)
                        {
                            switch (cryptoConfig.Key)
                            {
                            case ConfigKeys.ServerPFXChain:
                                certInfo.ServerKeyChainBase64PKCS12 = cryptoConfig.Value;
                                break;

                            case ConfigKeys.ServerCertificate:
                                certInfo.ServerBase64PKCS12 = cryptoConfig.Value;
                                break;

                            case ConfigKeys.ServerCertificatePassword:
                                certInfo.ServerCertificatePassword = cryptoConfig.Value;
                                break;

                            case ConfigKeys.CertificationAuthority:
                                certInfo.CertificateAuthorityBase64PKCS12 = cryptoConfig.Value;
                                break;

                            case ConfigKeys.CeritificationAuthorityPassword:
                                certInfo.CertificateAuthoryPassword = cryptoConfig.Value;
                                break;
                            }
                        }

                        // Check to see if any value is null.
                        if (
                            certInfo.ServerKeyChainBase64PKCS12.IsNullOrEmpty() ||
                            certInfo.CertificateAuthorityBase64PKCS12.IsNullOrEmpty() ||
                            certInfo.ServerBase64PKCS12.IsNullOrEmpty() ||
                            certInfo.CertificateAuthoryPassword.IsNullOrEmpty() ||
                            certInfo.ServerCertificatePassword.IsNullOrEmpty()
                            )
                        {
                            _logger.LogError("TG: One or more crypto parameters are invalid, please re-install.");
                            return(null);
                        }

                        var certificateAuthory = _cryptoService.LoadCertificate(
                            Convert.FromBase64String(certInfo.CertificateAuthorityBase64PKCS12),
                            certInfo.CertificateAuthoryPassword
                            );
                        var serverCertificate = _cryptoService.LoadCertificate(
                            Convert.FromBase64String(certInfo.ServerBase64PKCS12),
                            certInfo.ServerCertificatePassword
                            );

                        var baseOpenVPNConfigInJson =
                            _db.Single <Configuration>(x => x.Key == ConfigKeys.OpenVPNBaseConfig);
                        var baseOpenVPNConfig = JsonConvert.DeserializeObject <OpenVPNConfig>(baseOpenVPNConfigInJson.Value);

                        var listenerConfigInJson = _db.Single <Configuration>(x => x.Key == ConfigKeys.OpenVPNListeners);
                        var listeners            = JsonConvert.DeserializeObject <List <OpenVPNListener> >(listenerConfigInJson.Value);

                        // Make sure there's configuration information in the database.
                        if (baseOpenVPNConfig == null || listeners == null || listeners.Count == 0)
                        {
                            _logger.LogError("TG: Could not fetch OpenVPN config from the database. Using defaults.");
                            baseOpenVPNConfig = Defaults.OpenVPN.Value;
                            listeners         = Defaults.OpenVPNListeners;
                        }

                        // Write configurations for each.
                        foreach (var listener in listeners)
                        {
                            var localConfig = new OpenVPNConfig(_engine, _identity)
                            {
                                Listener = listener
                            };
                            configs.Add(localConfig);
                        }

                        // Write objects for each configuration
                        foreach (var cfg in configs)
                        {
                            cfg.CACert            = certificateAuthory;
                            cfg.ServerCert        = serverCertificate;
                            cfg.PKCS12Certificate = certInfo.ServerKeyChainBase64PKCS12;
                            cfg.AllowMultipleConnectionsFromSameClient = baseOpenVPNConfig.AllowMultipleConnectionsFromSameClient;
                            cfg.ClientToClient  = baseOpenVPNConfig.ClientToClient;
                            cfg.MaxClients      = baseOpenVPNConfig.MaxClients;
                            cfg.DhcpOptions     = baseOpenVPNConfig.DhcpOptions;
                            cfg.RedirectGateway = baseOpenVPNConfig.RedirectGateway;
                            cfg.PushedNetworks  = baseOpenVPNConfig.PushedNetworks;
                        }

                        // Return the list of configurations.
                        return(configs);
                    }
                }
            };

            // Try to return the value.
            if (processors.TryGetValue(type, out var value))
            {
                return(value());
            }

            // Failed, return null.
            return(null);
        }
コード例 #2
0
        public async Task <IActionResult> HandleOpenVPNConfigUpdate([FromBody] OpenVPNConfigUpdateRequest config)
        {
            if (!ModelState.IsValid || !config.Validate(out var errors))
            {
                _response.Errors.Add(Errors.VALIDATION_FAILED, errors);
                return(BadRequest(_response));
            }

            // OK bob, the basic schema is valid. Let's do some semantics checks now.
            // There is also no need to coppy result.ErrorMessages out into our buffer this time. If we're here, that means it all passed already.

            // Let's check the listeners.
            var networksAlreadySeen = new List <IPNetwork>();

            var listenersAlreadySeen = new Dictionary <int,
                                                       List <OpenVPNListener> >();

            var errorEncountered = false;

            foreach (var listener in config.Listeners)
            {
                var parsedNetwork = IPNetwork.Parse(listener.Network);
                var parsedAddress = IPAddress.Parse(listener.IPAddress);

                foreach (var network in networksAlreadySeen)
                {
                    Logger.LogDebug($"Checking if {network} overlaps with any already defined networks: {networksAlreadySeen.Count} already seen.");
                    // Uh oh, we got an overlap. No bueno.
                    if (!network.Contains(parsedNetwork) && !network.Equals(parsedNetwork))
                    {
                        continue;
                    }
                    ;

                    _response.Errors.Add(Errors.FIELD_OVERLAP, $"listeners.network:{network},{parsedNetwork}");
                    errorEncountered = true;
                    break;
                }

                networksAlreadySeen.Add(parsedNetwork);

                // If we got here, that means listeners do not have network overlaps.
                if (!errorEncountered)
                {
                    // Now, let's check for port overlaps / same listener being defined multiple times
                    listenersAlreadySeen.TryGetValue(listener.Port.Value, out var listOfexistingListenersOnPort);

                    // OK, there are other listeners on this port.
                    if (listOfexistingListenersOnPort != null)
                    {
                        foreach (var abstractedListener in listOfexistingListenersOnPort)
                        {
                            if (!abstractedListener.Protocol.Equals(listener.Protocol))
                            {
                                Logger.LogDebug("Protocols do not match, continuing search for conflicts.");
                                continue;
                            }

                            if (abstractedListener.IPAddress.Equals(IPAddress.Any.ToString()))
                            {
                                _response.Errors.Add(Errors.PORT_CONFLICT_FOUND, "0.0.0.0");
                                errorEncountered = true;
                                break;
                            }

                            // Duplicate listener found
                            if (abstractedListener.IPAddress.Equals(listener.IPAddress))
                            {
                                _response.Errors.Add(Errors.DUPLICATE_IP_AS_LISTENER_REQUEST, listener.IPAddress);
                                errorEncountered = true;
                                break;
                            }
                        }

                        // If we got here, it was sufficiently unique.
                        listOfexistingListenersOnPort.Add(listener);
                    }
                    else
                    {
                        listOfexistingListenersOnPort = new List <OpenVPNListener> {
                            listener
                        };
                        listenersAlreadySeen.Add(listener.Port.Value, listOfexistingListenersOnPort);
                    }
                }
            }

            if (HasErrors())
            {
                return(BadRequest(_response));
            }

            var baseConfig = new OpenVPNConfig(null, null)
            {
                AllowMultipleConnectionsFromSameClient = config.AllowMultipleConnectionsFromSameClient.Value,
                ClientToClient  = config.ClientToClient.Value,
                MaxClients      = config.MaxClients.Value,
                PushedNetworks  = config.PushedNetworks as List <string>,
                RedirectGateway = config.RedirectGateway as List <RedirectGatewayOptions>,
                DhcpOptions     = config.DhcpOptions as List <Tuple <DhcpOptions, string> >,
            };

            var allListeners = config.Listeners as List <OpenVPNListener>;

            // Let's commit it all to DB as required.
            await ConfigUtils.CreateOrUpdateConfig(Db, ConfigKeys.OpenVPNBaseConfig, JsonConvert.SerializeObject(baseConfig));

            await ConfigUtils.CreateOrUpdateConfig(Db, ConfigKeys.OpenVPNListeners, JsonConvert.SerializeObject(allListeners));

            // Now, we need to figure out if service restart will be needed.
            // However, since this is a 3rd party daemon -- our work is way easier. We can't make ANY changes at runtime.
            // TODO: Do a config diff before doing this, but that's skipped for now.

            var targetServiceType = typeof(OpenVPN);

            var service = _serviceManager.GetService(targetServiceType);

            // Let's get the parsed config out of the DB, and update svc state if needed.
            var reconciledConfig = _serviceConfigManager.Generate(targetServiceType);

            if (service.GetState() == ServiceState.Running)
            {
                // Yep, restart will be needed.
                _response.Message = Messages.SERVICE_RESTART_NEEDED;
            }

            service.SetConfig(reconciledConfig);

            _response.Result = config;

            return(Ok(_response));
        }
コード例 #3
0
        // TODO: Look into whether making this a responsibility of the service itself (generation) is a more sane appraoch
        // This would foster loose coupling
        private async Task <UserServiceResource> GenerateUserServiceResource(User user, Type type, IEnumerable <IServiceConfig> configs)
        {
            var serviceReference = new UserServiceResource();

            EnsureServiceAccess(user, type);

            // Giant hack, but hey, it works ┐(´∀`)┌ヤレヤレ
            switch (true)
            {
            case bool _ when type == typeof(HTTPProxy):
                // HTTPProxy has a global config, and thus only one instance
                // Guaranteed to not be null, so inspection is not needed.
                // ReSharper disable once AssignNullToNotNullAttribute
                var config  = (HTTPConfig)configs.First();
                var proxies = new List <string>();
                foreach (var listener in config.listeners)
                {
                    // Gotta translate 0.0.0.0 into something people can actually connect to
                    // This is currently replaced by the default outgoing IP, which should work assuming NAT port forwarding succeeded
                    // Or there is no NAT involved.
                    proxies.Add(await _ipResolver.Translate(listener.Item1) + ":" + listener.Item2);
                }

                serviceReference.AccessReference   = proxies;
                serviceReference.AccessCredentials = Messages.SPECTERO_USERNAME_PASSWORD;
                break;

            case bool _ when type == typeof(OpenVPN):
                // OpenVPN is a multi-instance service
                var allListeners = new List <OpenVPNListener>();

                OpenVPNConfig sanitizedOpenVPNConfig = null;

                foreach (var vpnConfig in configs)
                {
                    var castConfig = vpnConfig as OpenVPNConfig;
                    if (castConfig?.Listener != null)
                    {
                        // ReSharper disable once InconsistentNaming
                        var translatedIP = await _ipResolver.Translate(castConfig.Listener.IPAddress);

                        // This is done to force a translation of local addresses
                        castConfig.Listener.IPAddress = translatedIP.ToString();

                        allListeners.Add(castConfig.Listener);

                        // Setting it only once would be ideal, but eh -- overhead is low enough to make this work.
                        sanitizedOpenVPNConfig = castConfig;
                    }
                }

                if (!allListeners.IsNullOrEmpty())
                {
                    serviceReference.AccessConfig = await _razorLightEngine.CompileRenderAsync("OpenVPNUser", new OpenVPNUserConfig
                    {
                        Listeners  = allListeners,
                        User       = user,
                        Identity   = _identityProvider.GetGuid().ToString(),
                        BaseConfig = sanitizedOpenVPNConfig
                    });

                    serviceReference.AccessCredentials = user.CertKey.IsNullOrEmpty()
                            ? Messages.SPECTERO_USERNAME_PASSWORD
                            : user.CertKey;
                }

                break;
            }

            return(serviceReference);
        }