diff --git a/CHANGELOG b/CHANGELOG
index 85f86e7..265262a 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,10 @@
-2016-06-11 Version 1.0.4 (beta)
+2016-07-03 Version 1.0.5 (stable)
+ removed the RDS role, application server role and file storage service prerequisites
+ removed the creation of an rdp user ("Myrtille") on install (it was used for tests but is no longer relevant)
+ removed the configuration on the localhost rdp server
+ added automatic configuration of the firewall (websockets ports)
+
+2016-06-11 Version 1.0.4 (beta)
Added domain support to file transfer (see file transfer configuration in documentation)
Build set back to AnyCPU to have 64 bits registry hives loaded on 64 bits machines (fix a load user profile issue)
diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md
index b461291..d0faa8c 100644
--- a/DOCUMENTATION.md
+++ b/DOCUMENTATION.md
@@ -19,11 +19,9 @@ I hope you will enjoy Myrtille! :)
Special thanks to Catalin Trifanescu for its support.
## Prerequisites
-Ensure the following Windows Server Roles and Features are installed on the machine on which you want to install Myrtille:
-- Remote Destop Services role (formerly Terminal Services). Myrtille only requires the Remote Desktop Session Host. You can either setup it manually (see notes and limitations below) or double click the Myrtille "RDPSetup.reg" file for automatic configuration (import registry keys).
-- Web Server role (IIS). Myrtille also requires .NET 4.0, which can be installed separately (using the Myrtille setup bootstrapper or a standalone installation package) or as a IIS feature.
-- Applications Server role. Myrtille requires the Windows Processes activation service support, through HTTP, TCP and named pipes.
-- Files Storage Service role. Should be installed automatically if the above roles are installed. Myrtille requires the files server feature in order to allow to upload/download file(s) to the connected users documents folders.
+- IIS 7.0+ (Web Server role on Windows Servers)
+- .NET 4.0+ (Web Server role > Applications Development > ASP.NET 4.5 on Windows Server 2012; can also be installed separately using a standalone .NET 4.x installer)
+- Microsoft Visual C++ 2015 redistributables. **CAUTION ** on Windows Server 2012, it requires the system to be fully updated (Windows updates) first; see notes and limitations
## File transfer
Myrtille supports both local and network file storage. If you want your domain users to have access to their documents whatever the connected server, follow these steps:
@@ -34,31 +32,31 @@ Myrtille supports both local and network file storage. If you want your domain u
- In the settings tab, ensure the user doesn't have exclusive rights to the documents folder (otherwise Myrtille won't be able to access it)
## Network
-Add the following rules to the machine firewall:
-- "Myrtille Websockets": allow both directions TCP port 8181
-- "Myrtille Websockets Secured": allow both directions TCP port 8431
+The installer adds the following rules to the machine firewall:
+- "Myrtille Websockets": allow both directions TCP port 8181 (default)
+- "Myrtille Websockets Secured": allow both directions TCP port 8431 (default)
## Installation
+First ensure the prerequisites are met (see above).
+
All releases here: https://github.com/cedrozor/myrtille/releases
-- Setup.exe (preferred installation method): setup bootstrapper; automatically download and install .NET 4.0 and Microsoft Visual C++ 2015 (x86) redistributables (if not already installed), then install the Myrtille MSI package
+- Setup.exe (preferred installation method): setup bootstrapper; automatically download and install .NET 4.0 and Microsoft Visual C++ 2015 redistributables (if not already installed), then install the Myrtille MSI package
- Myrtille.msi: Myrtille MSI package (x86)
-If you have several RDP servers, you don't have to install Myrtille on each of them; you only have to configure them to be accessed by a Myrtille installation.
-
-You can either do it manually (see notes and limitations below) or copy and import the Myrtille "RDPSetup.reg" file over the servers.
-
## Security
If you want to use Myrtille through HTTPS (https://yourserver/myrtille), you have to create a self-signed SSL certificate or import a valid one (server side). Then, in order to use secure websockets (WSS), export this certificate into the Myrtille "ssl" folder, with private key, name "PKCS12Cert.pfx" and set a password that match the one defined into the Myrtille "Web.Config" file ("myrtille" by default).
-If not using Google Chrome (client side), see detailed comments regarding the security configuration into the Myrtille "Web.Config" file. You may have to add an exception for port 8431 (secure websockets) into your browser.
+If not using Google Chrome (client side), see detailed comments regarding the security configuration into the Myrtille "Web.Config" file. You may have to add an exception for the secured websockets port (default 8431) into your browser.
-In case of issues, ensure the port 8431 is not blocked by your firewall (or proxy, reverse proxy, VPN, etc.).
+In case of issues, ensure the secured websockets port (default 8431) is not blocked by your firewall (or proxy, reverse proxy, VPN, etc.).
## Configuration
Both the gateway and services have their own .NET config files; the gateway also uses XDT transform files to adapt the settings depending on the current solution configuration.
You may also play with the gateway "js/config.js" file settings to fine tune the configuration depending on your needs.
+Regarding the RDP server(s) configuration, Myrtille requires NLA to be disabled (see notes and limitations below). You can either do it manually or copy and import the Myrtille "RDPSetup.reg" file over the server(s).
+
## Code organization
- Myrtille.RDP: C++ code. FreeRDP rdp client; modified to forward the user input(s) and encode the session display into the configured image format(s). The modified code in FreeRDP is identified by region tags "#pragma region Myrtille" and "#pragma endregion".
- Myrtille.Common: C# code. Fleck Websockets library and common helpers.
@@ -112,7 +110,7 @@ This is a thing to consider if you want to isolate the web gateway from your int
## Notes and limitations
- On Windows Server 2008, you may have to install (manually) the Microsoft Visual C++ 2008 redistributables, required by OpenSSL (libeay32.dll/ssleay32.dll).
-- On Windows Server 2012, you may have issues installing the Microsoft Visual C++ 2015 redistributables (http://stackoverflow.com/questions/31536606/while-installing-vc-redist-x64-exe-getting-error-failed-to-configure-per-machi). To circumvent that, ensure your system is fully updated or try to install the package "Windows8.1-KB2999226-x64.msu" manually.
+- On Windows Server 2012, you may have issues installing the Microsoft Visual C++ 2015 redistributables (http://stackoverflow.com/questions/31536606/while-installing-vc-redist-x64-exe-getting-error-failed-to-configure-per-machi). To circumvent that, ensure your system is fully updated (Windows updates) first or try to install the package "Windows8.1-KB2999226-x64.msu" manually.
- Myrtille doesn't support clipboard and printer; they could however be enabled through FreeRDP virtual channels, given some additionnal code.
@@ -122,10 +120,6 @@ This is a thing to consider if you want to isolate the web gateway from your int
- In order to keep the installation simple, both the myrtille gateway and services are installed on the same machine. They do however conform to a distributed architecture; if needed, given some additionnal code, myrtille services could acts as a proxy, so the gateway could be installed and operate separately (this could be handy if the gateway should go into a DMZ).
-- The installer creates a test user on the local machine named "myrtille", password "/Passw1rd/"; feel free to remove it if unwanted. The user is automatically removed on uninstall.
-
-- The installer configures the RDP server on the local machine according to the Myrtille specifications (see above comment regarding NLA); any subsequent configuration changes may make Myrtille to dysfunction or stop working.
-
## Troubleshoot
First at all, ensure the Myrtille prerequisites are met (see "Prerequisites").
@@ -142,11 +136,11 @@ First at all, ensure the Myrtille prerequisites are met (see "Prerequisites").
- Ensure the network traffic (websockets and xmlhttp in particular) is not blocked by a firewall, proxy, reverse proxy, VPN or whatever.
- Ensure IIS is started and "Myrtille.Web" application is running on the "MyrtilleAppPool" application pool.
- Ensure .NET 4.0 is installed and the "MyrtilleAppPool" is running on it.
- - If using HTTPS with HTML5 rendering (hence secure websockets, WSS), ensure the TCP port 8431 is opened (see "Security").
+ - If using HTTPS with HTML5 rendering (hence secure websockets, WSS), ensure the secured websockets port (default 8431) is opened (see "Security").
- Ensure the "Myrtille.Services" Windows service (or console application if running under Visual Studio) is started.
- Ensure the RDP client ("FreeRDP.wfreerdp.exe") does exists (into the "Myrtille.Services" output folder, if running under Visual Studio, or into the "bin" folder otherwise); if not, you need to build the "Myrtille.RDP/FreeRDP.wfreerdp" project (or simply build all the solution).
- - Ensure the Microsoft Visual C++ 2015 (x86) redistributables are installed (and also Microsoft Visual C++ 2008 (x86) redistributables if on Windows Server 2008); they are required by the RDP client.
- - Check the RDP server configuration (**ensure NLA is disabled** (Myrtille supports standard RDP authentication only; see notes and limitations), does the user exists, is it a member of the "Remote Desktop Users" group, are Remote Desktop CALs valid?, etc.).
+ - Ensure the Microsoft Visual C++ 2015 redistributables are installed (and also Microsoft Visual C++ 2008 redistributables if on Windows Server 2008); they are required by the RDP client.
+ - Check the RDP server configuration (**ensure NLA is disabled** (Myrtille supports standard RDP authentication only; see notes and limitations), does the user exists, is it a member of the "Remote Desktop Users" group, are Remote Desktop CALs valid?, etc.). You can setup it automatically by importing the Myrtille "RDPSetup.reg" file into registry.
- Check the RDP server logs (and also the Windows events logs on the RDP server machine).
- Check the Windows events logs ("System", "Application", etc.), particulary regarding .NET.
- Retry with Myrtille logs enabled and check them (Myrtille "log" folder). You can change their verbosity level in config (but be warned it will affect peformance and flood the disk if setted too verbose).
diff --git a/Myrtille.Common/Helpers/FirewallHelper.cs b/Myrtille.Common/Helpers/FirewallHelper.cs
new file mode 100644
index 0000000..250eccb
--- /dev/null
+++ b/Myrtille.Common/Helpers/FirewallHelper.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Diagnostics;
+using NetFwTypeLib;
+
+namespace Myrtille.Helpers
+{
+ public static class FirewallHelper
+ {
+ public static void OpenFirewallPort(int port, string description)
+ {
+ try
+ {
+ // firewall manager
+ var TicfMgr = Type.GetTypeFromProgID("HNetCfg.FwMgr");
+ var icfMgr = (INetFwMgr)Activator.CreateInstance(TicfMgr);
+
+ // open port
+ var TportClass = Type.GetTypeFromProgID("HNetCfg.FWOpenPort");
+ var portClass = (INetFwOpenPort)Activator.CreateInstance(TportClass);
+
+ // current profile
+ var profile = icfMgr.LocalPolicy.CurrentProfile;
+
+ // set properties
+ portClass.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL;
+ portClass.Protocol = NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP;
+ portClass.Port = port;
+ portClass.Name = description;
+ portClass.Enabled = true;
+
+ // add the port to the ICF permissions list
+ profile.GloballyOpenPorts.Add(portClass);
+ }
+ catch (Exception exc)
+ {
+ Trace.TraceError("Failed to open firewall port ({0})", exc);
+ }
+ }
+
+ public static void CloseFirewallPort(int port)
+ {
+ try
+ {
+ // firewall manager
+ var TicfMgr = Type.GetTypeFromProgID("HNetCfg.FwMgr");
+ var icfMgr = (INetFwMgr)Activator.CreateInstance(TicfMgr);
+
+ // current profile
+ var profile = icfMgr.LocalPolicy.CurrentProfile;
+
+ // add the port to the ICF permissions list
+ profile.GloballyOpenPorts.Remove(port, NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP);
+ }
+ catch (Exception exc)
+ {
+ Trace.TraceError("Failed to remove firewall port ({0})", exc);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Myrtille.Common/Helpers/XmlTools.cs b/Myrtille.Common/Helpers/XmlTools.cs
new file mode 100644
index 0000000..9a31391
--- /dev/null
+++ b/Myrtille.Common/Helpers/XmlTools.cs
@@ -0,0 +1,114 @@
+using System.Xml;
+using System.Xml.XPath;
+
+namespace Myrtille.Helpers
+{
+ public static class XmlTools
+ {
+ public static XmlNode GetNode(
+ XmlNode parentNode,
+ string name)
+ {
+ XmlNode theNode = null;
+
+ if ((parentNode != null) &&
+ (parentNode.ChildNodes != null) &&
+ (parentNode.ChildNodes.Count > 0))
+ {
+ foreach (XmlNode node in parentNode.ChildNodes)
+ {
+ if (node.Name.ToUpper().Equals(name.ToUpper()))
+ {
+ theNode = node;
+ break;
+ }
+ }
+ }
+
+ return theNode;
+ }
+
+ public static XmlNode GetNode(
+ XPathNavigator navigator,
+ string path)
+ {
+ XmlNode node = null;
+
+ var iterator = navigator.Select(path);
+ if (iterator.Count == 1)
+ {
+ iterator.MoveNext();
+ node = ((IHasXmlNode)iterator.Current).GetNode();
+ }
+
+ return node;
+ }
+
+ public static string ReadConfigKey(
+ XmlNode parentNode,
+ string key)
+ {
+ if ((parentNode != null) &&
+ (parentNode.ChildNodes != null) &&
+ (parentNode.ChildNodes.Count > 0))
+ {
+ XmlNode theNode = null;
+
+ foreach (XmlNode node in parentNode.ChildNodes)
+ {
+ if ((node.Name.ToUpper().Equals("ADD")) &&
+ (node.Attributes != null) &&
+ (node.Attributes["key"] != null) &&
+ (node.Attributes["key"].Value.ToUpper().Equals(key.ToUpper())))
+ {
+ theNode = node;
+ break;
+ }
+ }
+
+ if ((theNode != null) &&
+ (theNode.Attributes != null) &&
+ (theNode.Attributes["value"] != null))
+ {
+ var theNodeValue = theNode.Attributes["value"];
+ return theNodeValue.Value;
+ }
+ }
+
+ return null;
+ }
+
+ public static void WriteConfigKey(
+ XmlNode parentNode,
+ string key,
+ string value)
+ {
+ if ((parentNode != null) &&
+ (parentNode.ChildNodes != null) &&
+ (parentNode.ChildNodes.Count > 0))
+ {
+ XmlNode theNode = null;
+
+ foreach (XmlNode node in parentNode.ChildNodes)
+ {
+ if ((node.Name.ToUpper().Equals("ADD")) &&
+ (node.Attributes != null) &&
+ (node.Attributes["key"] != null) &&
+ (node.Attributes["key"].Value.ToUpper().Equals(key.ToUpper())))
+ {
+ theNode = node;
+ break;
+ }
+ }
+
+ if ((theNode != null) &&
+ (theNode.Attributes != null) &&
+ (theNode.Attributes["value"] != null))
+ {
+ var theNodeValue = theNode.Attributes["value"];
+ theNodeValue.Value = value;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Myrtille.Common/Myrtille.Common.csproj b/Myrtille.Common/Myrtille.Common.csproj
index cd075f3..9de6387 100644
--- a/Myrtille.Common/Myrtille.Common.csproj
+++ b/Myrtille.Common/Myrtille.Common.csproj
@@ -103,10 +103,12 @@
Code
+
+
@@ -154,6 +156,17 @@
+
+
+ {58FBCF7C-E7A9-467C-80B3-FC65E8FCCA08}
+ 1
+ 0
+ 0
+ tlbimp
+ False
+ True
+
+
diff --git a/Myrtille.Setup/Myrtille.Setup.vdproj b/Myrtille.Setup/Myrtille.Setup.vdproj
index 0b3e78a..fa86f44 100644
--- a/Myrtille.Setup/Myrtille.Setup.vdproj
+++ b/Myrtille.Setup/Myrtille.Setup.vdproj
@@ -28,13 +28,13 @@
"Entry"
{
"MsmKey" = "8:_02A3E8F5A11247389E2ABAF9C960F248"
- "OwnerKey" = "8:_C8C1CA93127A4CFF84719DC54B07F45A"
+ "OwnerKey" = "8:_106A23CA68E948AB9DFA10C27C8D763A"
"MsmSig" = "8:_UNDEFINED"
}
"Entry"
{
"MsmKey" = "8:_02A3E8F5A11247389E2ABAF9C960F248"
- "OwnerKey" = "8:_106A23CA68E948AB9DFA10C27C8D763A"
+ "OwnerKey" = "8:_C8C1CA93127A4CFF84719DC54B07F45A"
"MsmSig" = "8:_UNDEFINED"
}
"Entry"
@@ -118,13 +118,13 @@
"Entry"
{
"MsmKey" = "8:_730F9A3A0F1A07BA06A442ADAD7A415A"
- "OwnerKey" = "8:_4DCF371A3A1D076CF90D60A9BB14DE7A"
+ "OwnerKey" = "8:_C8C1CA93127A4CFF84719DC54B07F45A"
"MsmSig" = "8:_UNDEFINED"
}
"Entry"
{
"MsmKey" = "8:_730F9A3A0F1A07BA06A442ADAD7A415A"
- "OwnerKey" = "8:_C8C1CA93127A4CFF84719DC54B07F45A"
+ "OwnerKey" = "8:_4DCF371A3A1D076CF90D60A9BB14DE7A"
"MsmSig" = "8:_UNDEFINED"
}
"Entry"
@@ -160,7 +160,7 @@
"Entry"
{
"MsmKey" = "8:_UNDEFINED"
- "OwnerKey" = "8:_730F9A3A0F1A07BA06A442ADAD7A415A"
+ "OwnerKey" = "8:_02A3E8F5A11247389E2ABAF9C960F248"
"MsmSig" = "8:_UNDEFINED"
}
"Entry"
@@ -172,7 +172,7 @@
"Entry"
{
"MsmKey" = "8:_UNDEFINED"
- "OwnerKey" = "8:_392E7B629F0F9B1D4A57F77B2D08CB9E"
+ "OwnerKey" = "8:_730F9A3A0F1A07BA06A442ADAD7A415A"
"MsmSig" = "8:_UNDEFINED"
}
"Entry"
@@ -184,7 +184,7 @@
"Entry"
{
"MsmKey" = "8:_UNDEFINED"
- "OwnerKey" = "8:_02A3E8F5A11247389E2ABAF9C960F248"
+ "OwnerKey" = "8:_392E7B629F0F9B1D4A57F77B2D08CB9E"
"MsmSig" = "8:_UNDEFINED"
}
}
@@ -319,7 +319,7 @@
"Sequence" = "3:1"
"Identifier" = "8:_A97BE628_DCEA_4ACD_8872_BC7D76C070A9"
"InstallerClass" = "11:TRUE"
- "CustomActionData" = "8:/TargetDir=\"[TARGETDIR]\\\""
+ "CustomActionData" = "8:/TargetDir=\"[TARGETDIR]\\\" /WSPort=\"[WSPORT]\" /WSSPort=\"[WSSPORT]\""
}
"{4AA51A2D-7D85-4A59-BA75-B0809FC8B380}:_65F5DA5C96624F5FB1094D4EA89321EB"
{
@@ -421,11 +421,6 @@
"AssemblyAsmDisplayName" = "8:log4net, Version=1.2.13.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL"
"ScatterAssemblies"
{
- "_02A3E8F5A11247389E2ABAF9C960F248"
- {
- "Name" = "8:log4net.dll"
- "Attributes" = "3:512"
- }
}
"SourcePath" = "8:log4net.dll"
"TargetName" = "8:"
@@ -472,11 +467,6 @@
"AssemblyAsmDisplayName" = "8:Myrtille.Services.Contracts, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"
"ScatterAssemblies"
{
- "_392E7B629F0F9B1D4A57F77B2D08CB9E"
- {
- "Name" = "8:Myrtille.Services.Contracts.dll"
- "Attributes" = "3:512"
- }
}
"SourcePath" = "8:Myrtille.Services.Contracts.dll"
"TargetName" = "8:"
@@ -523,11 +513,6 @@
"AssemblyAsmDisplayName" = "8:Myrtille.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"
"ScatterAssemblies"
{
- "_4DCF371A3A1D076CF90D60A9BB14DE7A"
- {
- "Name" = "8:Myrtille.Common.dll"
- "Attributes" = "3:512"
- }
}
"SourcePath" = "8:Myrtille.Common.dll"
"TargetName" = "8:"
@@ -773,14 +758,14 @@
"Name" = "8:Microsoft Visual Studio"
"ProductName" = "8:Myrtille"
"ProductCode" = "8:{B00BCC6F-3958-4726-9D99-DBAC78513C26}"
- "PackageCode" = "8:{116CEC78-AF35-488E-8D60-56E5333A0D10}"
+ "PackageCode" = "8:{38329484-F9B1-4E91-B61E-9BCA1D67250F}"
"UpgradeCode" = "8:{8B56253E-D535-4891-BA0D-13DE49BB64A0}"
"AspNetVersion" = "8:4.0.30319.0"
"RestartWWWService" = "11:FALSE"
"RemovePreviousVersions" = "11:TRUE"
"DetectNewerInstalledVersion" = "11:TRUE"
"InstallAllUsers" = "11:TRUE"
- "ProductVersion" = "8:1.0.4"
+ "ProductVersion" = "8:1.0.5"
"Manufacturer" = "8:Cedric Coste"
"ARPHELPTELEPHONE" = "8:"
"ARPHELPLINK" = "8:http://cedrozor.github.io/myrtille"
@@ -993,7 +978,7 @@
{
"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_9915AC3CC03B41A4A1E52FE4FB13435B"
{
- "Sequence" = "3:500"
+ "Sequence" = "3:400"
"DisplayName" = "8:Confirm Installation"
"UseDynamicProperties" = "11:TRUE"
"IsDependency" = "11:FALSE"
@@ -1013,6 +998,262 @@
}
}
}
+ "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_D3E8896C53074675A1C07EC446BD1CB4"
+ {
+ "Sequence" = "3:300"
+ "DisplayName" = "8:Textboxes (A)"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:\\VsdCustomText1Dlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "BannerText"
+ {
+ "Name" = "8:BannerText"
+ "DisplayName" = "8:#1014"
+ "Description" = "8:#1114"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:2"
+ "Value" = "8:Firewall configuration"
+ "DefaultValue" = "8:#1214"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "BodyText"
+ {
+ "Name" = "8:BodyText"
+ "DisplayName" = "8:#1015"
+ "Description" = "8:#1115"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:2"
+ "Value" = "8:Ports to open for the HTML5 websockets communication"
+ "DefaultValue" = "8:#1215"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Edit1Label"
+ {
+ "Name" = "8:Edit1Label"
+ "DisplayName" = "8:#1046"
+ "Description" = "8:#1146"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:2"
+ "Value" = "8:Standard port"
+ "DefaultValue" = "8:#1246"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Edit1Property"
+ {
+ "Name" = "8:Edit1Property"
+ "DisplayName" = "8:#1050"
+ "Description" = "8:#1150"
+ "Type" = "3:14"
+ "ContextData" = "8:Public"
+ "Attributes" = "3:2"
+ "Setting" = "3:2"
+ "Value" = "8:WSPORT"
+ "DefaultValue" = "8:EDITA1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Edit1Value"
+ {
+ "Name" = "8:Edit1Value"
+ "DisplayName" = "8:#1054"
+ "Description" = "8:#1154"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:2"
+ "Value" = "8:8181"
+ "DefaultValue" = "8:"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Edit1Visible"
+ {
+ "Name" = "8:Edit1Visible"
+ "DisplayName" = "8:#1042"
+ "Description" = "8:#1142"
+ "Type" = "3:5"
+ "ContextData" = "8:1;True=1;False=0"
+ "Attributes" = "3:0"
+ "Setting" = "3:0"
+ "Value" = "3:1"
+ "DefaultValue" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Edit2Label"
+ {
+ "Name" = "8:Edit2Label"
+ "DisplayName" = "8:#1047"
+ "Description" = "8:#1147"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:2"
+ "Value" = "8:Secured port"
+ "DefaultValue" = "8:#1247"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Edit2Property"
+ {
+ "Name" = "8:Edit2Property"
+ "DisplayName" = "8:#1051"
+ "Description" = "8:#1151"
+ "Type" = "3:14"
+ "ContextData" = "8:Public"
+ "Attributes" = "3:2"
+ "Setting" = "3:2"
+ "Value" = "8:WSSPORT"
+ "DefaultValue" = "8:EDITA2"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Edit2Value"
+ {
+ "Name" = "8:Edit2Value"
+ "DisplayName" = "8:#1055"
+ "Description" = "8:#1155"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:2"
+ "Value" = "8:8431"
+ "DefaultValue" = "8:"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Edit2Visible"
+ {
+ "Name" = "8:Edit2Visible"
+ "DisplayName" = "8:#1043"
+ "Description" = "8:#1143"
+ "Type" = "3:5"
+ "ContextData" = "8:1;True=1;False=0"
+ "Attributes" = "3:0"
+ "Setting" = "3:0"
+ "Value" = "3:1"
+ "DefaultValue" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Edit3Label"
+ {
+ "Name" = "8:Edit3Label"
+ "DisplayName" = "8:#1048"
+ "Description" = "8:#1148"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:1"
+ "Value" = "8:#1248"
+ "DefaultValue" = "8:#1248"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Edit3Property"
+ {
+ "Name" = "8:Edit3Property"
+ "DisplayName" = "8:#1052"
+ "Description" = "8:#1152"
+ "Type" = "3:14"
+ "ContextData" = "8:Public"
+ "Attributes" = "3:2"
+ "Setting" = "3:2"
+ "Value" = "8:EDITA3"
+ "DefaultValue" = "8:EDITA3"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Edit3Value"
+ {
+ "Name" = "8:Edit3Value"
+ "DisplayName" = "8:#1056"
+ "Description" = "8:#1156"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:1"
+ "Value" = "8:"
+ "DefaultValue" = "8:"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Edit3Visible"
+ {
+ "Name" = "8:Edit3Visible"
+ "DisplayName" = "8:#1044"
+ "Description" = "8:#1144"
+ "Type" = "3:5"
+ "ContextData" = "8:1;True=1;False=0"
+ "Attributes" = "3:0"
+ "Setting" = "3:0"
+ "Value" = "3:0"
+ "DefaultValue" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Edit4Label"
+ {
+ "Name" = "8:Edit4Label"
+ "DisplayName" = "8:#1049"
+ "Description" = "8:#1149"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:1"
+ "Value" = "8:#1249"
+ "DefaultValue" = "8:#1249"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Edit4Property"
+ {
+ "Name" = "8:Edit4Property"
+ "DisplayName" = "8:#1053"
+ "Description" = "8:#1153"
+ "Type" = "3:14"
+ "ContextData" = "8:Public"
+ "Attributes" = "3:2"
+ "Setting" = "3:2"
+ "Value" = "8:EDITA4"
+ "DefaultValue" = "8:EDITA4"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Edit4Value"
+ {
+ "Name" = "8:Edit4Value"
+ "DisplayName" = "8:#1057"
+ "Description" = "8:#1157"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:1"
+ "Value" = "8:"
+ "DefaultValue" = "8:"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Edit4Visible"
+ {
+ "Name" = "8:Edit4Visible"
+ "DisplayName" = "8:#1045"
+ "Description" = "8:#1145"
+ "Type" = "3:5"
+ "ContextData" = "8:1;True=1;False=0"
+ "Attributes" = "3:0"
+ "Setting" = "3:0"
+ "Value" = "3:0"
+ "DefaultValue" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_E241C459E9064B7DB39DE004CEB20533"
{
"Sequence" = "3:100"
diff --git a/Myrtille.Web/Global.asax.cs b/Myrtille.Web/Global.asax.cs
index 1949d26..32c95db 100644
--- a/Myrtille.Web/Global.asax.cs
+++ b/Myrtille.Web/Global.asax.cs
@@ -18,9 +18,9 @@ limitations under the License.
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Security.Cryptography.X509Certificates;
-using System.ServiceModel.Configuration;
using System.Web;
using System.Web.Configuration;
using log4net.Config;
@@ -48,65 +48,73 @@ protected void Application_Start(
object sender,
EventArgs e)
{
- // logger
- XmlConfigurator.Configure();
+ try
+ {
+ // logger
+ XmlConfigurator.Configure();
- // application cache
- Application[HttpApplicationStateVariables.Cache.ToString()] = Context.Cache;
+ // application cache
+ Application[HttpApplicationStateVariables.Cache.ToString()] = Context.Cache;
- // remote sessions auto-incremented counter
- Application[HttpApplicationStateVariables.RemoteSessionsCounter.ToString()] = 0;
+ // remote sessions auto-incremented counter
+ Application[HttpApplicationStateVariables.RemoteSessionsCounter.ToString()] = 0;
- // remote sessions managers
- Application[HttpApplicationStateVariables.RemoteSessionsManagers.ToString()] = new Dictionary();
+ // remote sessions managers
+ Application[HttpApplicationStateVariables.RemoteSessionsManagers.ToString()] = new Dictionary();
- // start a websocket server on a standard port; the port must be opened into your firewall (incoming traffic)
- var webSocketServerPort = 8181;
- if (!string.IsNullOrEmpty(WebConfigurationManager.AppSettings["WebSocketServerPort"]))
- {
- webSocketServerPort = int.Parse(WebConfigurationManager.AppSettings["WebSocketServerPort"]);
- }
- Application[HttpApplicationStateVariables.WebSocketServerPort.ToString()] = webSocketServerPort;
- var server = new WebSocketServer(string.Format("ws://0.0.0.0:{0}", webSocketServerPort));
- new RemoteSessionSocketServer(this, server);
-
- // to use secure websockets, you must first export your IIS SSL certificate in .PFX format, with the private key and a password
- // save it into the application "ssl" folder under the name "PKCS12Cert.pfx" (or a name matching the Web.config key "WebSocketServerCertificateFilename")
- // make sure the file have the appropriate rights for IIS/ASP.NET to read it (NETWORK SERVICE and USER granted read access)
- // then update the Web.config key "WebSocketServerCertificatePassword" with your password (you may encrypt Web.config if you wish...)
-
- // certificate filename
- var webSocketServerCertificateFilename = "ssl\\PKCS12Cert.pfx";
- if (!string.IsNullOrEmpty(WebConfigurationManager.AppSettings["webSocketServerCertificateFilename"]))
- {
- webSocketServerCertificateFilename = WebConfigurationManager.AppSettings["webSocketServerCertificateFilename"];
- }
-
- // check existence of a certificate (note that ".pfx" MIME type is blocked for download for the application...)
- if (!File.Exists(Path.Combine(Server.MapPath("~"), webSocketServerCertificateFilename)))
- {
- Application[HttpApplicationStateVariables.WebSocketServerPortSecured.ToString()] = null;
- }
- else
- {
- // start a websocket server on a secured port; the port must be opened into your firewall (incoming traffic)
- var webSocketServerPortSecured = 8431;
- if (!string.IsNullOrEmpty(WebConfigurationManager.AppSettings["WebSocketServerPortSecured"]))
+ // start a websocket server on a standard port; the port must be opened into your firewall (incoming traffic)
+ var webSocketServerPort = 8181;
+ if (!string.IsNullOrEmpty(WebConfigurationManager.AppSettings["WebSocketServerPort"]))
{
- webSocketServerPortSecured = int.Parse(WebConfigurationManager.AppSettings["WebSocketServerPortSecured"]);
+ webSocketServerPort = int.Parse(WebConfigurationManager.AppSettings["WebSocketServerPort"]);
}
- Application[HttpApplicationStateVariables.WebSocketServerPortSecured.ToString()] = webSocketServerPortSecured;
- server = new WebSocketServer(string.Format("wss://0.0.0.0:{0}", webSocketServerPortSecured));
+ Application[HttpApplicationStateVariables.WebSocketServerPort.ToString()] = webSocketServerPort;
+ var server = new WebSocketServer(string.Format("ws://0.0.0.0:{0}", webSocketServerPort));
+ new RemoteSessionSocketServer(this, server);
+
+ // to use secure websockets, you must first export your IIS SSL certificate in .PFX format, with the private key and a password
+ // save it into the application "ssl" folder under the name "PKCS12Cert.pfx" (or a name matching the Web.config key "WebSocketServerCertificateFilename")
+ // make sure the file have the appropriate rights for IIS/ASP.NET to read it (NETWORK SERVICE and USER granted read access)
+ // then update the Web.config key "WebSocketServerCertificatePassword" with your password (you may encrypt Web.config if you wish...)
- // certificate password
- var WebSocketServerCertificatePassword = "myrtille";
- if (!string.IsNullOrEmpty(WebConfigurationManager.AppSettings["WebSocketServerCertificatePassword"]))
+ // certificate filename
+ var webSocketServerCertificateFilename = "ssl\\PKCS12Cert.pfx";
+ if (!string.IsNullOrEmpty(WebConfigurationManager.AppSettings["webSocketServerCertificateFilename"]))
{
- WebSocketServerCertificatePassword = WebConfigurationManager.AppSettings["WebSocketServerCertificatePassword"];
+ webSocketServerCertificateFilename = WebConfigurationManager.AppSettings["webSocketServerCertificateFilename"];
}
- server.Certificate = new X509Certificate2(Path.Combine(Server.MapPath("~"), webSocketServerCertificateFilename), WebSocketServerCertificatePassword);
- new RemoteSessionSocketServer(this, server);
+ // check existence of a certificate (note that ".pfx" MIME type is blocked for download for the application...)
+ if (!File.Exists(Path.Combine(Server.MapPath("~"), webSocketServerCertificateFilename)))
+ {
+ Application[HttpApplicationStateVariables.WebSocketServerPortSecured.ToString()] = null;
+ }
+ else
+ {
+ // start a websocket server on a secured port; the port must be opened into your firewall (incoming traffic)
+ var webSocketServerPortSecured = 8431;
+ if (!string.IsNullOrEmpty(WebConfigurationManager.AppSettings["WebSocketServerPortSecured"]))
+ {
+ webSocketServerPortSecured = int.Parse(WebConfigurationManager.AppSettings["WebSocketServerPortSecured"]);
+ }
+ Application[HttpApplicationStateVariables.WebSocketServerPortSecured.ToString()] = webSocketServerPortSecured;
+ server = new WebSocketServer(string.Format("wss://0.0.0.0:{0}", webSocketServerPortSecured));
+
+ // certificate password
+ var WebSocketServerCertificatePassword = "myrtille";
+ if (!string.IsNullOrEmpty(WebConfigurationManager.AppSettings["WebSocketServerCertificatePassword"]))
+ {
+ WebSocketServerCertificatePassword = WebConfigurationManager.AppSettings["WebSocketServerCertificatePassword"];
+ }
+
+ server.Certificate = new X509Certificate2(Path.Combine(Server.MapPath("~"), webSocketServerCertificateFilename), WebSocketServerCertificatePassword);
+ new RemoteSessionSocketServer(this, server);
+ }
+ }
+ catch (Exception exc)
+ {
+ Trace.TraceError("Failed to start Myrtille.Web application ({0})", exc);
+ throw;
}
}
}
diff --git a/Myrtille.Web/Web.Debug.config b/Myrtille.Web/Web.Debug.config
index 3848072..5ab5365 100644
--- a/Myrtille.Web/Web.Debug.config
+++ b/Myrtille.Web/Web.Debug.config
@@ -28,4 +28,7 @@
-->
+
+
+
\ No newline at end of file
diff --git a/Myrtille.Web/src/WebInstaller.cs b/Myrtille.Web/src/WebInstaller.cs
index 814e3a8..86840cb 100644
--- a/Myrtille.Web/src/WebInstaller.cs
+++ b/Myrtille.Web/src/WebInstaller.cs
@@ -24,6 +24,7 @@ limitations under the License.
using System.IO;
using System.Security.AccessControl;
using System.Windows.Forms;
+using System.Xml;
using Myrtille.Helpers;
namespace Myrtille.Web
@@ -58,8 +59,6 @@ public override void Install(
try
{
- Process process = null;
-
// register Myrtille.Web to local IIS
if (!IISHelper.IsIISApplicationPoolExists("MyrtilleAppPool"))
@@ -82,38 +81,45 @@ public override void Install(
PropagationFlags.None,
AccessControlType.Allow);
- // create a default rdp user (myrtille) on the local server
+ // websockets ports
- AccountHelper.CreateLocalUser(
- "Myrtille",
- "Myrtille User",
- "/Passw1rd/",
- true,
- true);
+ int wsPort;
+ if (!int.TryParse(Context.Parameters["WSPort"], out wsPort))
+ {
+ wsPort = 8181;
+ }
- // add myrtille to the windows users group
+ int wssPort;
+ if (!int.TryParse(Context.Parameters["WSSPort"], out wssPort))
+ {
+ wssPort = 8431;
+ }
- AccountHelper.AddLocalUserToGroup(
- "Myrtille",
- AccountHelper.GetUsersGroupName());
+ // load config
- // add myrtille to the remote desktop users group
+ var config = new XmlDocument();
+ var configPath = Path.Combine(Path.GetFullPath(Context.Parameters["targetdir"]), "Web.config");
+ config.Load(configPath);
- AccountHelper.AddLocalUserToGroup(
- "Myrtille",
- AccountHelper.GetRemoteDesktopUsersGroupName());
+ // update settings
+
+ var navigator = config.CreateNavigator();
+
+ var node = XmlTools.GetNode(navigator, "/configuration/appSettings");
+ if (node != null)
+ {
+ XmlTools.WriteConfigKey(node, "WebSocketServerPort", wsPort.ToString());
+ XmlTools.WriteConfigKey(node, "WebSocketServerPortSecured", wssPort.ToString());
+ }
- // import the rdp registry keys required by myrtille on the local server
+ // save config
- process = new Process();
+ config.Save(configPath);
- #if !DEBUG
- process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
- #endif
+ // open ports
- process.StartInfo.FileName = "regedit.exe";
- process.StartInfo.Arguments = "/s \"" + Path.Combine(Path.GetFullPath(Context.Parameters["targetdir"]), "RDPSetup.reg") + "\"";
- process.Start();
+ FirewallHelper.OpenFirewallPort(wsPort, "Myrtille Websockets");
+ FirewallHelper.OpenFirewallPort(wssPort, "Myrtille Websockets Secured");
Trace.TraceInformation("Installed Myrtille.Web");
}
@@ -167,9 +173,39 @@ private void DoUninstall()
IISHelper.DeleteIISApplicationPool("MyrtilleAppPool");
}
- // delete myrtille user
+ // websockets ports
+
+ int wsPort = 8181;
+ int wssPort = 8431;
+
+ // load config
+
+ var config = new XmlDocument();
+ var configPath = Path.Combine(Path.GetFullPath(Context.Parameters["targetdir"]), "Web.config");
+ config.Load(configPath);
+
+ // read settings
+
+ var navigator = config.CreateNavigator();
+
+ var node = XmlTools.GetNode(navigator, "/configuration/appSettings");
+ if (node != null)
+ {
+ if (!int.TryParse(XmlTools.ReadConfigKey(node, "WebSocketServerPort"), out wsPort))
+ {
+ wsPort = 8181;
+ }
+
+ if (!int.TryParse(XmlTools.ReadConfigKey(node, "WebSocketServerPortSecured"), out wssPort))
+ {
+ wssPort = 8431;
+ }
+ }
+
+ // close ports
- AccountHelper.DeleteLocalUser("Myrtille");
+ FirewallHelper.CloseFirewallPort(wsPort);
+ FirewallHelper.CloseFirewallPort(wssPort);
Trace.TraceInformation("Uninstalled Myrtille.Web");
}
diff --git a/README.md b/README.md
index 7a436d9..a9429b5 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# Myrtille
-Myrtille provides a simple and fast access to remote desktops and applications through a web browser (without any plugin, extension or configuration).
+Myrtille provides a simple and fast access to remote desktops and applications through a web browser, without any plugin, extension or configuration.
+
+Technically, Myrtille is an HTTP(S) to RDP gateway.
## How does it works?
It works by forwarding the user inputs (keyboard, mouse, touchscreen) from a web browser to an HTTP(S) gateway, then up to an RDP client which maintain a session with an RDP server.
@@ -12,20 +14,20 @@ More information into the DOCUMENTATION.md file.
## Features
- HTML4 and HTML5 support
+- HTTP(S) to RDP gateway
- File transfer
- WebP compression
## Requirements
-- Client: a HTML4 or HTML5 browser
-- Server: a RDP enabled computer, IIS 7.0+ and .NET 4.0+ (see DOCUMENTATION.md for related roles and features)
+- HTTP(S) client: any HTML4 or HTML5 browser (starting from IE6!)
+- HTTP(S) to RDP gateway: IIS 7.0+ and .NET 4.0+ (see DOCUMENTATION.md for gateway prerequisites)
+- RDP server: any RDP enabled computer (preferably Windows Server but can also be Windows XP, 7, 8, 10. see DOCUMENTATION.md for configuration)
## Build
See DOCUMENTATION.md.
## Installation
All releases here: https://github.com/cedrozor/myrtille/releases
-- Setup.exe (preferred installation method): setup bootstrapper; automatically download and install .NET 4.0 and Microsoft Visual C++ 2015 (x86) redistributables (if not already installed), then install the Myrtille MSI package
-- Myrtille.msi: Myrtille MSI package (x86)
See DOCUMENTATION.md for more details.