Open sdbbs opened 1 year ago
Ok, I found also https://github.com/wallix/redemption/blob/future/docs/manual_to_redemption.rst - this explains a bit more:
Redemption is a versatile RDP proxy, meaning one will connect to remote desktops through Redemption. This allows to centralize remote connection creating a single end point for several desktops.
Running
For a local test, the usual options are
-n
and-f
. The first option prevents Redemption from forking in the background, and the second makes sure no other instance is running. ::$ sudo /usr/local/bin/rdpproxy -nf
And now what ? If everything went ok, you should be facing a waiting daemon ! You need two more things; first a client to connect to Redemption, second a server with RDP running (a Windows server, Windows XP Pro, etc.).
Redemption uses a hook file to get its target, username and password. This file is in
utils/authhook.py
and as you noticed it is written in Python. Two dictionnaries are important, the first one stores passwords, the second one stores the targets.
Ok, so I guess that blue screen above is part of the "allows to centralize remote connection creating a single end point for several desktops"
Unfortunately, as far as authhook.py
goes - How to use this project · Issue #25 · wallix/redemption:
The part about
authhook.py
is out of date and has been replaced bypassthrough.py
. Currently, the internal bouncer module is not accessible without modification ofpassthrough.py
and only the interactive login is used.
Also, I've found rdpclient
:
$ rdpclient --help
Usage: rdpclient [options]
-h, --help produce help message
-v, --version show software version
-t, --target-device=<address>
-u, --username=<username>
-p, --password=<password>
-P, --port=<port>
-a, --inactivity-time=<milliseconds> milliseconds inactivity before sreenshot
-m, --max-time=<milliseconds> maximum milliseconds before sreenshot
-s, --screen-output=<path> png screenshot path
-r, --record-path=<path> dump socket path
-V, --vnc[={on|off}] enable vnc instead of rdp
-l, --lcg[={on|off}] use LCGRandom
-b, --load-balance-info=<data>
-n, --ini=<path> load ini filename
-c, --cert-check=<number>
0 = fails if certificates doesn't match or miss.
1 = fails if certificate doesn't match, succeed if no known certificate
2 = succeed if certificates exists (not checked), fails if missing.
3 = always succeed.
--verbose=<verbosity>
This at least has something that looks like target address, and even has a record path; however:
$ rdpclient --target_device="127.0.0.1"
Bad option at parameter 1 (--target_device=127.0.0.1)
Why a "bad option"? How is "address" supposed to be formatted, then?
And how is one supposed to work with rdpclient
?
The configuration of rdpproxy
is done from the ini file in /etc/rdpproxy/rdpproxy.ini
which can be generated with rdpproxy --print-default-ini
. redrec
is also configured from this file, framerate
and notimestamp
may interest you.
For a connection to a target without going through the interactive module (the screen you see), you need to specify a password and a login of the form {username}@{ip}
when connecting to the proxy. This is not done by remmina without having configured it (the example given with xfreerdp should work).
The proxy then sends this information to passthrough.py
which responds with a list of variables to initialize a module with these options. To enable recording, you must uncomment the line kv[u'is_rec'] = u'1'
.
rdpclient
will check that a connection is possible, but you will not be able to do anything with it. There is theoretically projects/qtclient
that would be suitable, but the recording doesn't work. With the work on an automatable client, it's not impossible that qtclient is taken over to serve as a helper and that the recording part will be reworked.
Many thanks for the response, @jonathanpoelen - much appreciated!
The configuration of
rdpproxy
is done from the ini file in/etc/rdpproxy/rdpproxy.ini
which can be generated withrdpproxy --print-default-ini
.redrec
is also configured from this file,framerate
andnotimestamp
may interest you.
Excellent, that is crucial info I was missing! I appreciate very much that this project provided the ./tools/packager.py
, so I can build a .deb package; managed to also put all other executables in it (see unix.se:734205) - however now I realized, even after installing that .deb with dpkg -i
, I did not get a /etc/rdpproxy/rdpproxy.ini
. So I had to do:
sudo bash -c 'rdpproxy --print-default-ini > /etc/rdpproxy/rdpproxy.ini'
... to create this file; I will now look into how to configure it properly for my use case.
For a connection to a target without going through the interactive module (the screen you see), you need to specify a password and a login of the form
{username}@{ip}
when connecting to the proxy. This is not done by remmina without having configured it (the example given with xfreerdp should work).
Excellent, this is also crucal info! I will look into configuring remmina
for the required for of the login - will probably have to test with xfreerdp
while doing that, too.
The proxy then sends this information to
passthrough.py
which responds with a list of variables to initialize a module with these options. To enable recording, you must uncomment the linekv[u'is_rec'] = u'1'
.
Awesome, this explains things a lot more for me! However, I'd like to clarify: does passthrough.py
, after it initializes a module, get involved with (RDP) traffic, or recording, in any way? Or does that part (handling traffic and recording) get completely overtaken by rdpproxy
once initialized?
Btw, great work from this project on conceptualizing a proxy that would be able to handle (and record) both VNC and RDP - having looked a bit into remote protocols, that is an idea I had thought for a while would be awesome, but also "too hard" to do - great to see this project brought the concept to implementation stage!
rdpclient
will check that a connection is possible, but you will not be able to do anything with it.
Ok, I need some more clarification here. In the discussion so far, I've had the following model in my head:
passthough.py
, it keeps running listening to a file socket so it can talk to rdpproxy
rdpproxy
- it first reads through /etc/rdpproxy/rdpproxy.ini
to get configured (including any info for target remote RDP server, and recording), then starts listening on a local IP port on that machine for incomming RDP connectionsremmina
, xfreerdp
) and point it to the IP and port of the machine where rdpproxy
is listening; upon connection, rdpproxy
asks passthrough.py
for initialization, gets initialized, and from that point on, passes the RDP username/password credentials to the target remote server; once authorized, the session with the target remote server starts, and is reproduced interactively in the RDP client window (as well as being recorded by rdpproxy
if so configured).So, in this model, if I use rdpclient
as ... well ... and RDP client, then why would I "not be able to do anything with it"? If it just initializes the RDP conneciton, and shows the session in a window, and the recording is taken over by rdpproxy
- well, that's all I would want to do with it, quite the opposite from "not being able to do anything with it"?! Unless I'm misunderstanding something in the mechanism of how things work here (which is also quite likely).
There is theoretically
projects/qtclient
that would be suitable, but the recording doesn't work. With the work on an automatable client, it's not impossible that qtclient is taken over to serve as a helper and that the recording part will be reworked.
Great, thanks for noting that - I also managed to find qtclient
, and managed to compile it; and also experienced the recording does not work, so it's great to have that confirmed. I will jot down my experience with qtclient
in the next post, as it was otherwise quite difficult to find any info on it.
Right - so, I realized qtclient
exists, and I tried to compile it. On Ubuntu 20.04 I also needed these packages to complete the compilation:
sudo apt install qtbase5-dev qt5-default qt5-qmake qtcreator qtbase5-examples qtbase5-doc-html
sudo apt install libphonon4qt5-dev
The compilation otherwise completed with:
cd projects/qtclient
bjam qtclient
Now, the thing is, the Ubuntu machine I was trying this on was headless, so I wanted to also put the executable, qt5client
(note the 5
in the name of the executable), in the .deb with the other redemption
programs. This turned out to be quite gruelling, I did not have fun with bjam
, but I managed somehow - see https://stackoverflow.com/questions/75340407/bjam-build-subfolder-project-change-directory-to-subfolder-project-before-bui
Anyways, at first, I just tried qt5client --help
from a terminal, and among other text, I got these messages:
WARNING: bool Phonon::FactoryPrivate::createBackend() phonon backend plugin could not be loaded
WARNING: bool Phonon::FactoryPrivate::createBackend() phonon backend plugin could not be loaded
WARNING: bool Phonon::FactoryPrivate::createBackend() phonon backend plugin could not be loaded
WARNING: bool Phonon::FactoryPrivate::createBackend() phonon backend plugin could not be loaded
WARNING: Phonon::createPath: Cannot connect Phonon::MediaObject ( no objectName ) to Phonon::AudioOutput ( no objectName ).
After consulting https://askubuntu.com/questions/715587/phonon-backend-plugin-could-not-be-loaded, the solution for this was:
sudo apt install phonon4qt5 # not enough
sudo apt install phonon4qt5-backend-gstreamer phonon4qt5-backend-vlc
Right, so now, this is the result of qt5client --help
for me:
$ qt5client --help
Client ReDemPtion Help menu.
-h, --help Show help
-v, --version Show version
========= Connection =========
-u, --username=<value> Set target session user name
-p, --password=<value> Set target session user password
-i, --ip=<value> Set target IP address
-P, --port=<value> Set port to use on target
========= Verbose =========
--rdpdr[={on|off}] Active rdpdr logs
--rdpsnd[={on|off}] Active rdpsnd logs
--cliprdr[={on|off}] Active cliprdr logs
--graphics[={on|off}] Active graphics logs
--printer[={on|off}] Active printer logs
--rdpdr-dump[={on|off}] Actives rdpdr logs and dump brute rdpdr PDU
--cliprd-dump[={on|off}] Actives cliprdr logs and dump brute cliprdr PDU
--basic-trace[={on|off}] Active basic-trace logs
--connection[={on|off}] Active connection logs
--rail-order[={on|off}] Active rail-order logs
--asynchronous-task[={on|off}] Active asynchronous-task logs
--capabilities[={on|off}] Active capabilities logs
--rail[={on|off}] Active rail logs
--rail-dump[={on|off}] Actives rail logs and dump brute rail PDU
========= Protocol =========
--vnc Set connection mod to VNC
--rdp Set connection mod to RDP (default).
--remote-app[={on|off}] Connection as remote application.
--remote-exe=command Connection as remote application and set the line command.
--span[={on|off}] Span the screen size on local screen
--enable-clipboard[={on|off}] Enable clipboard sharing
--enable-nla[={on|off}] Enable NLA protocol
--enable-tls[={on|off}] Enable TLS protocol
--tls-min-level=<value> Minimal TLS protocol level
--tls-max-level=<value> Maximal TLS protocol level allowed
--show_common_cipher_list[={on|off}] Show TLS Cipher List
--cipher_string=<value> Set TLS Cipher allowed for TLS <= 1.2
--enable-sound[={on|off}] Enable sound
--enable-fullwindowdrag[={on|off}] Enable full window draging
--enable-menuanimations[={on|off}] Enable menu animations
--enable-theming[={on|off}] Enable theming
--enable-cursor-shadow[={on|off}] Enable cursor shadow
--enable-cursorsettings[={on|off}] Enable cursor settings
--enable-font-smoothing[={on|off}] Enable font smoothing
--enable-desktop-composition[={on|off}] Enable desktop composition
--vnc-applekeyboard[={on|off}] Set keyboard compatibility mod with apple VNC server
--keep_alive_frequence=<value> Set timeout to send keypress to keep the session alive
--remotefx[={on|off}] enable remotefx
========= Client =========
--width=<value> Set screen width
--height=<value> Set screen height
--bpp=bit_per_pixel Set bit per pixel (8, 15, 16, 24, 32)
--keylayout=<value> Set windows keylayout
--enable-record[={on|off}] Enable session recording as .wrm movie
--persist Set connection to persist
--timeout=time Set timeout response before to disconnect in millisecond
--share-dir=directory Set directory path on local disk to share with your session.
--remote-dir=directory Remote working directory
WARNING (2809660/2809660) -- readdir error: (1504) Exception ERR_TRANSPORT_READ_FAILED no: 1504
QObject::connect: No such slot QtOptions::deletePressed() in src/qt_input_output_api/qt_graphics_components/qt_options_window.hpp:241
QObject::connect: No such slot QtOptions::deletePressed() in src/qt_input_output_api/qt_graphics_components/qt_options_window.hpp:241
free(): invalid size
Aborted (core dumped)
Notice that it crashes/segfaults in the end (free(): invalid size
, Aborted (core dumped)
); this was worrying, I thought it was possibly because of the "QObject::connect: No such slot QtOptions::deletePressed()
" messages. However, then I just decided to try it like this:
$ qt5client --width=1366 --height=768
WARNING (2809944/2809944) -- readdir error: (1504) Exception ERR_TRANSPORT_READ_FAILED no: 1504
QObject::connect: No such slot QtOptions::deletePressed() in src/qt_input_output_api/qt_graphics_components/qt_options_window.hpp:241
QObject::connect: No such slot QtOptions::deletePressed() in src/qt_input_output_api/qt_graphics_components/qt_options_window.hpp:241
... and in spite of those messages, I still get a GUI window! Nice - here is a screenshot:
Some things I have noted:
--temporary
switch, so that whatever settings I enter for the connection do not get savedqt5client --width=1366 --height=768
with the intent to set the client (that is, my end) window size, that is ignored, and what matters is actually the GUI setting Options/View/Resolution, which is a dropdown with five predefined settings. It would have been great if --width=1366 --height=768
would have been translated in an extra setting "1366 * 768" in that dropdown, that would be automatically selected by default if --width
/--height
was specified on the command line Otherwise, qt5client
worked fine for me in connecting to a remote server and providing me a window of the session; the only glitch I noticed that it registers mouse right-click in Windows Terminal with selected text (which makes a copy and removes selection), but when you right-click again to paste text, the text does not get pasted.
However, I also wanted to try recording, so I tried first:
qt5client --width=1366 --height=768 --enable-record=on
I made an RDP session with a remote server here, and after a while disconnected, but I could not find any new capture files that got generated.
Then I realized I have to click Options/General/Record Movie as on the screenshot; then again, I made an RDP session with a remote server, and after a while disconnected - and I looked up anything that looked like a capture file; I found them here:
$ ls -la $HOME/src/redemption.git/projects/qtclient/DATA/replay
total 13380
drwxrwxr-x 2 USER USER 4096 Feb 4 04:51 .
drwxrwxr-x 6 USER USER 4096 Feb 4 04:29 ..
-rwxrwxrwx 1 USER USER 6706523 Feb 4 05:29 'Sat Feb 4 05:29:14 2023-Replay-000000.wrmred-l8RfuB.tmp'
-rwxrwxrwx 1 USER USER 25 Feb 4 05:29 'Sat Feb 4 05:29:14 2023-Replay.mwrmred-QZCX5z.tmp'
Well, both the location and the naming format was kind of unexpected for me; also, I cannot tell why there are two files, where one has *mwrm*
in the name and is only 25 bytes, and the other has *wrm*
in the name and is many bytes ...
Anyways, so I tried to convert these to redrec
, as per the README:
$ redrec --encryption=enable -f --video-codec mp4 -i $HOME/src/redemption_git/projects/qtclient/DATA/replay/'Sat Feb 4 05:29:14 2023-Replay.mwrmred-QZCX5z.tmp' -o $PWD/test.mp4
Output file is "$HOME/Desktop/test.mp4".
wrm file not found in mwrm file
$ redrec --encryption=enable -f --video-codec mp4 -i $HOME/src/redemption.git/projects/qtclient/DATA/replay/'Sat Feb 4 05:29:14 2023-Replay-000000.wrmred-l8RfuB.tmp' -o $PWD/test.mp4
Output file is "$HOME/Desktop/test.mp4".
decrypt failed: with id=1504 ERR_TRANSPORT_READ_FAILED
There are no new files generated with either of these commands; I guess this confirms that recording with qtclient
does not yet work.
Yup, I think that was about it for notes about first-time qtclient
use; in all, its pretty cool most of its functionality works fine, it's just the recording that is a show stopper for me, otherwise it's only smaller UI glitches that I see as problems; overall, pretty good!
Among the executables, only rdpproxy
, redrec
and rdpinichecker
are really useful. The others are for testing purposes.
rdpproxy.ini
is not installed because it is not needed to run the proxy and it generates conflicts when updating the package. The --print-default-ini
option is mainly used to find out the default variables and values used by the proxy. You could simply have a file that contains only the values you are interested in. By the way, you can use the --config-file
option if you want multiple configurations or have an ini file in a different location.
I know that the README gives rather little information and some of the docs are outdated (this is clearly visible with last issues where I explain things that should be in it), so we should take some time to complete this.
Awesome, this explains things a lot more for me! However, I'd like to clarify: does passthrough.py, after it initializes a module, get involved with (RDP) traffic, or recording, in any way? Or does that part (handling traffic and recording) get completely overtaken by rdpproxy once initialized?
All the recording part will be done by the proxy, passthrough.py is used to provide configurations and target information, but does not handle RDP or VNC traffic. It has options like closing session, but passthrough.py does not send them. passthrough.py is basic, the idea is that people make their own version for their own needs (a note somewhere indicates where to find the exchanged values).
Ok, I need some more clarification here. In the discussion so far, I've had the following model in my head: I start up passthough.py, it keeps running listening to a file socket so it can talk to rdpproxy
Yes
I start up rdpproxy - it first reads through /etc/rdpproxy/rdpproxy.ini to get configured (including any info for target remote RDP server, and recording), then starts listening on a local IP port on that machine for incomming RDP connections
Yes. The RDP server information is mostly protocol configuration, most of the ini options can also be sent via passthrough.py if target specific configurations are needed.
Then I'd start up an RDP client (remmina, xfreerdp) and point it to the IP and port of the machine where rdpproxy is listening; upon connection, rdpproxy asks passthrough.py for initialization, gets initialized, and from that point on, passes the RDP username/password credentials to the target remote server; once authorized, the session with the target remote server starts, and is reproduced interactively in the RDP client window (as well as being recorded by rdpproxy if so configured).
Yes
So, in this model, if I use rdpclient as ... well ... and RDP client, then why would I "not be able to do anything with it"?
Your model assumes that clients necessarily display a window, rdpclient does not. The name is misleading, but in our vision of a world without direct rendering, it was natural :p. It will be removed soon because our new test tool will do the same thing and more (with a much less misleading name).
Right - so, I realized qtclient exists, and I tried to compile it. On Ubuntu 20.04 I also needed these packages to complete the compilation: sudo apt install qtbase5-dev qt5-default qt5-qmake qtcreator qtbase5-examples qtbase5-doc-html sudo apt install libphonon4qt5-dev
I think there is too much dependency and that qtbase5-dev is enough. qt5-qmake
is not needed since we don't use qmake
, qtcreator
is an IDE, qtbase5-examples
are code examples and qtbase5-doc-html
is just doc.
libphonon4qt5-dev
is what handles the sound part, the warnings on that are not important if you don't need the sound through RDP. Note on that, the sound is not in the recordings, but there must be a way at system level to record the sound of an application. qtclient could do this recording, but it would be a separate file that would have to be merged with the final video (it's easy with ffmpeg in cli).
To have qtclient in one package, I think it's best to make another one on the side. But if you want, you can add cd projects/qtclient; bjam qtclient
/ cd projects/qtclient; bjam install
in packaging/template/debian/rules
, add the dependencies in the packaging/targets/
files and the new executable in packaging/template/debian/redemption.install
(the last step may be useless thanks to %PREFIX%/bin/*
).
For .tmp files, this is a sign that the recording is still in progress (I think it is not stopped at the time of disconnection). It's normally recoverable by renaming the files and manually writing the mwrm, but I end up with an incomplete video. I'm thinking of doing a quick fix next week about stopping the recording, but if it's not enough, it will wait.
Many thanks again, @jonathanpoelen - excellent information!
Btw, I got a working setup with recording, and I will write about that in the next post.
Among the executables, only
rdpproxy
,redrec
andrdpinichecker
are really useful. The others are for testing purposes.
Got it, good to know!
rdpproxy.ini
is not installed because it is not needed to run the proxy and it generates conflicts when updating the package.
Thanks, good to know - especially since at first I was suspecting I did something wrong when building the .deb file.
You could simply have a file that contains only the values you are interested in.
I appreciate that functionality - good to know it is there!
All the recording part will be done by the proxy, passthrough.py is used to provide configurations and target information, but does not handle RDP or VNC traffic. It has options like closing session, but passthrough.py does not send them. passthrough.py is basic, the idea is that people make their own version for their own needs (a note somewhere indicates where to find the exchanged values).
Excellent - thanks for confirming that, good to know that I wasn't way too off in understanding how things generally work. I also ended up "independently confirming" (before reading your answer) that "the idea is that people make their own version for their own needs", in that in my working procedure, I had to hack passthrough.py
to get things to work.
Also thanks for reviewing my model - I have just recently learned that Github supports Mermaid diagrams, so I've drawn my understanding of the proxying process, in my use context: the RDP client I use on my machine, communicates with Redemption (rdpproxy + passthrough.py) on another machine on the same local network, to proxy the traffic to an RDP server (here, a port forward of a Windows 10 PC with Remote Desktop enabled):
%%{init: {'theme':'base'}}%%
flowchart LR
subgraph Border
direction LR
subgraph PC1["<div style='text-align: center;'>PC 1<br/><code>192.168.RDP.CLIENT</code></div>"]
direction LR
IP1["#nbsp;"]:::nodisplaypad;
RdpClient["RDP Client<br/>(<code>remmina</code>, <code>xfreerdp</code> ...)"]:::wht;
end
subgraph PC2["<div style='text-align: center;'>PC 2</div>"]
direction TB
%%IP2["192.168.RDP.PROXY"]:::blank;
subgraph PC2Contents["<div style='text-align: center;'><code>192.168.RDP.PROXY</code></div>"]
direction LR
RdpProxyPort("<code>port 3389</code>"):::port;
RdpProxyA["<code><b>rdpproxy -nf</b></code>"]:::wht;
RdpProxyPort <==> RdpProxyA;
FileSocket("File socket<br/><code>/tmp/redemption-sesman-sock</code>"):::wht;
RdpProxyB["<code><b>passthrough.py</b></code>"]:::wht;
RdpProxyA <--> FileSocket;
FileSocket <--> RdpProxyB;
end
end
RdpClient <==> RdpProxyPort;
subgraph CloudContainer[ ]
direction LR
%% [How to add a new shape? · Issue #1500 · mermaid-js/mermaid](https://github.com/mermaid-js/mermaid/issues/1500)
CloudInternetNode("<div style='position:absolute;left:-0px;top:-8px;color:#35;bbb;font-size:120px;'>#9729;#65039;</div><div style='position:relative;z-index:100;color:black;padding:18px;'>#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;Internet#nbsp;#nbsp;#nbsp;<br/>#nbsp;</div>");
end
RdpProxyA <==> CloudInternetNode;
subgraph PC3["<div style='text-align: center;'>PC 3</div>"]
direction TB
%%IP2["192.168.RDP.PROXY"]:::blank;
subgraph PC3Contents["<div style='text-align: center;'><code>209.85.RDP.SERVER</code></div>"]
direction TB
RdpServer["RDP Server<br/>(Target Windows PC)"]:::blank;
RdpServerPort("<code>port 13389</code>"):::port;
%%RdpServer <--> RdpServerPort; %% https://github.com/mermaid-js/mermaid/issues/2509
end
end
CloudInternetNode <==> RdpServerPort;
end
%% Defining Class Styles
%% background-image:"" passes, but not with any text inside quotes
%% node that classes here get defined with "#graph-div " prepended!
%% however, seemingly <img> and such are included via foreignObject, so cannot refer to these classes
classDef BorderClass fill:#fff,stroke:#fff,stroke-width:4px,color:#fff,stroke-dasharray: 0 4,margin:0,padding:0;
classDef ContainerClass position:relative,padding:0,margin:0,fill:#fff,color:#000,stroke:none;
classDef ImgNodeClass fill:#fff,padding:0,margin:0,stroke:none,margin:0;
classDef wht fill:#fff,color:black,stroke:#000;
classDef blank fill:#fff,color:none,stroke:none;
classDef nodisplaypad display:none;
classDef port color:#500,fill:#fff,stroke:#c80,stroke-width:2px,font-weight:normal;
%% Custom Styles
%% Assigning Nodes to Classes
%% class gets applied to all childer, too!
class Border BorderClass;
class CloudContainer ContainerClass;
class CloudInternetNode ImgNodeClass;
class PC1,PC2,PC3 wht;
class PC2Contents,PC3Contents blank;
Your model assumes that clients necessarily display a window, rdpclient does not. The name is misleading,
Ah, that explains things! Indeed, it seems there is a naming conflict there ... although I didn't even get to thinking about rdpclient
displaying a window, I got confused already when it refused to parse the --target_device
command line argument :)
... and indeed, in my working setup, I use remmina
to display the window (as on the diagram above).
... but in our vision of a world without direct rendering, it was natural :p. It will be removed soon because our new test tool will do the same thing and more (with a much less misleading name).
Please, PLEASE do not abandon your vision of a world without direct rendering!!!! In the setup I got working, (rdpproxy + passthrough.py) might as well run on a headless machine! So please leave an option for headless testing in the coming rdpclient replacement ...
I think there is too much dependency and that qtbase5-dev is enough.
qt5-qmake
is not needed since we don't useqmake
,qtcreator
is an IDE,qtbase5-examples
are code examples andqtbase5-doc-html
is just doc.
Awesome, great to know this - I just copy-pasted those lines from a tutorial somewhere, did not even get to thinking if those are too many packages.
libphonon4qt5-dev
is what handles the sound part, the warnings on that are not important if you don't need the sound through RDP. Note on that, the sound is not in the recordings, but there must be a way at system level to record the sound of an application. qtclient could do this recording, but it would be a separate file that would have to be merged with the final video (it's easy with ffmpeg in cli).
Good to know - I do not use sound in my recording, but love that the option is there - and so I'm glad I compiled with libphonon4qt5-dev
.
To have qtclient in one package, I think it's best to make another one on the side.
I would have gladly done so, but looking at the ./tools/packager.py
script, I couldn't figure out if it supported building of a qtclient
package. Does an option for building a, say, redemption-qtclient.deb package easily exist currently?
But if you want, you can add
cd projects/qtclient; bjam qtclient
/cd projects/qtclient; bjam install
inpackaging/template/debian/rules
, add the dependencies in thepackaging/targets/
files and the new executable inpackaging/template/debian/redemption.install
(the last step may be useless thanks to%PREFIX%/bin/*
).
Great, thanks for this. It turns out, the procedure I've described in https://stackoverflow.com/questions/75340407/bjam-build-subfolder-project-change-directory-to-subfolder-project-before-bui, while it handles qt5client correctly, it messes up paths for redrec
so it cannot run anymore; I've updated my answer in that post with your instructions.
For .tmp files, this is a sign that the recording is still in progress (I think it is not stopped at the time of disconnection). It's normally recoverable by renaming the files and manually writing the mwrm, but I end up with an incomplete video. I'm thinking of doing a quick fix next week about stopping the recording, but if it's not enough, it will wait.
Good to know - now that I can confirm recording with rdpproxy -nf
(and passthrough.py
) works(forme), I don't really need to use qt5client, so I'm fine with waiting for the recording fix for qt5client :)
Also, in my working setup, I can confirm that whatever .tmp files are captured, are in the end converted to (m)wrm files.
Thanks again for the feedback - and for the great software!
Right, so I managed to get recording with rdpproxy
going; again, this is the diagram of the use context - the RDP client I use on my machine (remmina
, communicates with Redemption (rdpproxy
+ passthrough.py
) on another machine on the same local network, to proxy the traffic to an RDP server (here, a port forward of a Windows 10 PC with Remote Desktop enabled):
%%{init: {'theme':'base'}}%%
flowchart LR
subgraph Border
direction LR
subgraph PC1["<div style='text-align: center;'>PC 1<br/><code>192.168.RDP.CLIENT</code></div>"]
direction LR
IP1["#nbsp;"]:::nodisplaypad;
RdpClient["RDP Client<br/>(<code>remmina</code>, <code>xfreerdp</code> ...)"]:::wht;
end
subgraph PC2["<div style='text-align: center;'>PC 2</div>"]
direction TB
%%IP2["192.168.RDP.PROXY"]:::blank;
subgraph PC2Contents["<div style='text-align: center;'><code>192.168.RDP.PROXY</code></div>"]
direction LR
RdpProxyPort("<code>port 3389</code>"):::port;
RdpProxyA["<code><b>rdpproxy -nf</b></code>"]:::wht;
RdpProxyPort <==> RdpProxyA;
FileSocket("File socket<br/><code>/tmp/redemption-sesman-sock</code>"):::wht;
RdpProxyB["<code><b>passthrough.py</b></code>"]:::wht;
RdpProxyA <--> FileSocket;
FileSocket <--> RdpProxyB;
end
end
RdpClient <==> RdpProxyPort;
subgraph CloudContainer[ ]
direction LR
%% [How to add a new shape? · Issue #1500 · mermaid-js/mermaid](https://github.com/mermaid-js/mermaid/issues/1500)
CloudInternetNode("<div style='position:absolute;left:-0px;top:-8px;color:#35;bbb;font-size:120px;'>#9729;#65039;</div><div style='position:relative;z-index:100;color:black;padding:18px;'>#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;Internet#nbsp;#nbsp;#nbsp;<br/>#nbsp;</div>");
end
RdpProxyA <==> CloudInternetNode;
subgraph PC3["<div style='text-align: center;'>PC 3</div>"]
direction TB
%%IP2["192.168.RDP.PROXY"]:::blank;
subgraph PC3Contents["<div style='text-align: center;'><code>209.85.RDP.SERVER</code></div>"]
direction TB
RdpServer["RDP Server<br/>(Target Windows PC)"]:::blank;
RdpServerPort("<code>port 13389</code>"):::port;
%%RdpServer <--> RdpServerPort; %% https://github.com/mermaid-js/mermaid/issues/2509
end
end
CloudInternetNode <==> RdpServerPort;
end
%% Defining Class Styles
%% background-image:"" passes, but not with any text inside quotes
%% node that classes here get defined with "#graph-div " prepended!
%% however, seemingly <img> and such are included via foreignObject, so cannot refer to these classes
classDef BorderClass fill:#fff,stroke:#fff,stroke-width:4px,color:#fff,stroke-dasharray: 0 4,margin:0,padding:0;
classDef ContainerClass position:relative,padding:0,margin:0,fill:#fff,color:#000,stroke:none;
classDef ImgNodeClass fill:#fff,padding:0,margin:0,stroke:none,margin:0;
classDef wht fill:#fff,color:black,stroke:#000;
classDef blank fill:#fff,color:none,stroke:none;
classDef nodisplaypad display:none;
classDef port color:#500,fill:#fff,stroke:#c80,stroke-width:2px,font-weight:normal;
%% Custom Styles
%% Assigning Nodes to Classes
%% class gets applied to all childer, too!
class Border BorderClass;
class CloudContainer ContainerClass;
class CloudInternetNode ImgNodeClass;
class PC1,PC2,PC3 wht;
class PC2Contents,PC3Contents blank;
A redemption
package was built from git on PC 2 (192.168.RDP.PROXY) with:
./tools/packager.py --build-package --force-build
... and installed with:
sudo dpkg -i ../redemption_10.4.41+focal_amd64.deb
... and /etc/rdpproxy/rdpproxy.ini
was generated with:
sudo bash -c 'rdpproxy --print-default-ini > /etc/rdpproxy/rdpproxy.ini'
At this point, I reviewed rdpproxy.ini
, and was surprised that I did not find target server settings there either (as there weren't any in the options listed by rdpproxy --help
). I expected I should be able to specify target server settings in a file somewhere - and It turns out, I had to hack passthrough.py
to do that.
However, there are still important settings in rdpproxy.ini
- and one that really bit me was the setting that actually activates recording; even after I setup a working connection, I couldn't get any recording, and the log kept saying "Front::can_be_start_capture: Capture is not necessary". So I had to dig through the source to find src/front/front.hpp - and:
bool is_capture_necessary()
{
return (this->ini.get<cfg::video::allow_rt_without_recording>()
|| this->ini.get<cfg::globals::is_rec>()
|| !bool(this->ini.get<cfg::video::disable_keyboard_log>() & KeyboardLogFlags::syslog)
|| ::contains_kbd_or_ocr_pattern(this->ini.get<cfg::context::pattern_kill>())
|| ::contains_kbd_or_ocr_pattern(this->ini.get<cfg::context::pattern_notify>()));
}
At first, I found allow_rt_without_recording
confusing (as its help says "Allow real-time view (4 eyes) without session recording enabled in the authorization", and I wanted to enable recording, not "allow ... view ... without session recording ..."), and I thought I should enable is_rec
in the rdpproxy.ini
config file instead - the problem being, that rdpproxy.ini
did not contain any reference to is_rec
. So I added it manually, but that did not make any difference (so probably is_rec
is just a runtime parameter, but one stored in cfg::...
). Then I set allow_rt_without_recording
- and finally recording started!
That being said, here is what I changed in the rdpproxy.ini
as a diff - some lines I did not change, but I commented them anyways as I thought they were interesting:
--- rdpproxy.original.ini 2023-02-06 06:41:51.910624593 +0100
+++ /etc/rdpproxy/rdpproxy.ini 2023-02-06 06:59:32.045115305 +0100
@@ -53,14 +53,14 @@
#_advanced
#close_timeout = 600
-# Session record options.
+# Session record options. # note
# min = 0, max = 2
# 0: No encryption (faster).
# 1: No encryption, with checksum.
# 2: Encryption enabled.
# When session records are encrypted, they can be read only by the WAB where they have been generated.
#_advanced
-#trace_type = 1
+#trace_type = 1 # note
#_advanced
#listen_address = 0.0.0.0
@@ -152,6 +152,10 @@
#_hidden
#minimal_memory_available_before_connection_silently_closed = 100
+# is_rec - NOT originally present in output from sudo bash -c 'rdpproxy --print-default-ini > /etc/rdpproxy/rdpproxy.ini'
+# apparently to enable recording of video
+#is_rec = 1 # apparently does not get registered, try allow_rt_without_recording instead
+
[client]
# bg-BG, bg-BG.latin, bs-Cy, cs-CZ, cs-CZ.programmers, cs-CZ.qwerty, cy-GB, da-DK, de-CH, de-DE, de-DE.ibm, el-GR, el-GR.220, el-GR.220_latin, el-GR.319, el-GR.319_latin, el-GR.latin, el-GR.polytonic, en-CA.fr, en-CA.multilingual, en-GB, en-IE, en-IE.irish, en-US, en-US.dvorak, en-US.dvorak_left, en-US.dvorak_right, en-US.international, es-ES, es-ES.variation, es-MX, et-EE, fi-FI.finnish, fo-FO, fr-BE, fr-BE.fr, fr-CA, fr-CH, fr-FR, hr-HR, hu-HU, is-IS, it-IT, it-IT.142, iu-La, kk-KZ, ky-KG, lb-LU, lt-LT, lt-LT.ibm, lv-LV, lv-LV.qwerty, mi-NZ, mk-MK, mn-MN, mt-MT.47, mt-MT.48, nb-NO, nl-BE, nl-NL, pl-PL, pl-PL.programmers, pt-BR.abnt, pt-BR.abnt2, pt-PT, ro-RO, ru-RU, ru-RU.typewriter, se-NO, se-NO.ext_norway, se-SE, se-SE, se-SE.ext_finland_sweden, sk-SK, sk-SK.qwerty, sl-SI, sr-Cy, sr-La, sv-SE, tr-TR.f, tr-TR.q, tt-RU, uk-UA, uz-Cy
@@ -1029,7 +1033,7 @@
[video]
-# Specifies the type of data to be captured:
+# Specifies the type of data to be captured: # note
# min = 0, max = 15
# 0x00: none
# 0x01: png
@@ -1038,17 +1042,17 @@
# Note: values can be added (enable all: 0x01 + 0x02 + 0x08 = 0x0b)
#_advanced
#_hex
-#capture_flags = 11
+#capture_flags = 11 # note - default OK
-# Frame interval.
-# (in 1/10 seconds)
+# Frame interval. # note
+# (in 1/10 seconds) # note - max is 10 FPS
#_advanced
-#png_interval = 10
+#png_interval = 10 # note
-# Time between 2 wrm movies.
+# Time between 2 wrm movies. # note
# (in seconds)
#_advanced
-#break_interval = 600
+#break_interval = 600 # note
# Number of png captures to keep.
# min = 0
@@ -1058,14 +1062,17 @@
# maxlen = 4096
#_hidden
#hash_path = /var/rdpproxy/hash
+hash_path = /path/to/extern_disk/rdpproxy/hash
# maxlen = 4096
#_hidden
#record_tmp_path = /var/rdpproxy/tmp
+record_tmp_path = /path/to/extern_disk/rdpproxy/tmp
# maxlen = 4096
#_hidden
#record_path = /var/rdpproxy/recorded/rdp
+record_path = /path/to/extern_disk/rdpproxy/recorded
# Disable keyboard log:
# (Please see also "Keyboard input masking level" in "session_log".)
@@ -1101,20 +1108,20 @@
#_hex
#disable_file_system_log = 1
-# The method by which the proxy RDP establishes criteria on which to chosse a color depth for native video capture:
+# The method by which the proxy RDP establishes criteria on which to chosse a color depth for native video capture: # note
# min = 0, max = 1
# 0: 24-bit
# 1: 16-bit
#_advanced
-#wrm_color_depth_selection_strategy = 1
+#wrm_color_depth_selection_strategy = 1 # maybe need to make this 24-bit?
-# The compression method of native video capture:
+# The compression method of native video capture: # note
# min = 0, max = 2
# 0: no compression
# 1: gzip
# 2: snappy
#_advanced
-#wrm_compression_algorithm = 1
+#wrm_compression_algorithm = 1 # note: default 1: gzip, ok
# Needed to play a video with old ffplay or VLC v1.
# Note: Useless with mpv, MPlayer or VLC v2.
@@ -1123,23 +1130,24 @@
#_display_name=Bogus VLC frame rate
#bogus_vlc_frame_rate = 1
-#_advanced
-#codec_id = mp4
+#_advanced # note
+#codec_id = mp4 # note
# min = 1, max = 120
# min = 0
#_advanced
#_display_name=Frame rate
#framerate = 5
+framerate = 25
-# FFmpeg options for video codec. See https://trac.ffmpeg.org/wiki/Encode/H.264
+# FFmpeg options for video codec. See https://trac.ffmpeg.org/wiki/Encode/H.264 # note
# /!\ Some browsers and video decoders don't support crf=0
#_advanced
-#ffmpeg_options = crf=35 preset=superfast
+#ffmpeg_options = crf=35 preset=superfast # note
# value: 0 or 1
#_advanced
-#notimestamp = 0
+#notimestamp = 0 # refers to the timestamp in upper left corner of video
# min = 0, max = 2
# 0: Disabled. When replaying the session video, the content of the RDP viewer matches the size of the client's desktop
@@ -1151,16 +1159,19 @@
# value: 0 or 1
#_advanced
#play_video_with_corrupted_bitmap = 0
+play_video_with_corrupted_bitmap = 1
# Allow real-time view (4 eyes) without session recording enabled in the authorization
# value: 0 or 1
#allow_rt_without_recording = 0
+allow_rt_without_recording = 1
# Allow to control permissions on recorded files with octal number
# (is in octal or symbolic mode format (as chmod Linux command))
# max = 777, min = 0
#_hidden
#file_permissions = 440
+file_permissions = 644
[audit]
So, it seems that by default, these settings have break_interval = 600
which is "Time between 2 wrm movies", which as I understanding, means that the video capture will happen in 10 minute chunks (probably to allow for long captures, so you only lose the last 10 minutes if you run out of space on disk).
The notimestamp
refers to this graphical timestamp added to upper left corner of the video (the cool thing is, you can leave notimestamp = 0
in the ini file during rdpproxy
capture, and then change it to notimestamp = 1
during the redrec
video conversion phase, and the timestamp will be gone from the final video):
Anyways, now I needed to enter the target server credentials somewhere; and that ended up being in passthrough.py
: a subclass MyAuthentifierSharedData(AuthentifierSharedData)
was implemented with a method to (re)enforce the desired target settings, and changes to ACLPassthrough.start()
were made so that this method is called after the initial "RDP blue screen" exchange, and to handle assignment of target_port
- here is a diff:
diff --git a/tools/passthrough/passthrough.py b/tools/passthrough/passthrough.py
index d276754ca..3c6043a43 100755
--- a/tools/passthrough/passthrough.py
+++ b/tools/passthrough/passthrough.py
@@ -192,11 +192,29 @@ class AuthentifierSharedData():
return self.shared.get(key) == MAGICASK
+class MyAuthentifierSharedData(AuthentifierSharedData):
+ def __init__(self, conn):
+ # init parent class
+ super(MyAuthentifierSharedData, self).__init__(conn)
+ self.target ettings_enforce_hardcoded()
+
+ def target_settings_enforce_hardcoded(self):
+ # override settings
+ self.shared[u'target_device'] = "209.85.RDP.SERVER.com_13389" # treating it as just a label
+ self.shared[u'target_host'] = "209.85.RDP.SERVER.com"
+ self.shared[u'target_port'] = "13389"
+ self.shared[u'target_login'] = "TARGET_USERNAME"
+ self.shared[u'target_password'] = "TARGET_PASSWORD"
+ #self.shared[u'login'] # maybe for the internal blue starting RDP screen shown on RDP client?
+ #self.shared[u'ip_client'] # filled automatically, this is the IP of the RDP client that connected to the rdpproxy that talks to us
+
+
class ACLPassthrough():
def __init__(self, conn, addr):
self.proxy_conx = conn
self.addr = addr
- self.shared = AuthentifierSharedData(conn)
+ #self.shared = AuthentifierSharedData(conn)
+ self.shared = MyAuthentifierSharedData(conn)
def interactive_target(self, data_to_send):
data_to_send.update({ u'module' : u'interactive_target' })
@@ -239,8 +257,18 @@ class ACLPassthrough():
def start(self):
+ # NOTE: this start() method, runs only AFTER RDP blue screen has appeared on the RDP Client end,
+ # and the user has clicked to log in!
+ # NOTE: self.shared.receive_data actually changes self.shared.shared settings!
+ # unfortunately if we don't have it run, then other settings than the target ones
+ # are not initialized correctly ...
_status, _error = self.shared.receive_data()
+ # if we intervene with MyAuthentifierSharedData checking here, we'll mess up initialization;
+ # so instead, re-enforce the target settings afterwards
+
+ # this is likely the response to initial connection from RDP client,
+ # which instructs RDP proxy to serve the starting blue screen
device = "<host>$<application path>$<working dir>$<args> for Application"
login = self.shared.get(u'login', MAGICASK) or MAGICASK
host = self.shared.get(u'real_target_device', MAGICASK) or MAGICASK
@@ -271,6 +299,12 @@ class ACLPassthrough():
self.shared.shared[u'real_target_device'] = host
kv = interactive_data
+ if isinstance(self.shared, MyAuthentifierSharedData):
+ # re-enforce target settings
+ self.shared.target_settings_enforce_hardcoded()
+
+ print("passthrough start self.shared: {}".format(self.shared.shared))
+
# selector_data = {
# u'target_login': 'Proxy\\Administrator\x01login 2\x01login 3',
# u'target_device': '10.10.44.27\x01device 2\x01device 3',
@@ -283,7 +317,10 @@ class ACLPassthrough():
kv[u'record_filebase'] = datetime.now().strftime("%Y-%m-%d/%H:%M-") + str(uuid.uuid4())
kv[u'login'] = self.shared.get(u'target_login')
kv[u'proto_dest'] = "RDP"
- kv[u'target_port'] = "3389"
+ if not( isinstance(self.shared, MyAuthentifierSharedData) ):
+ kv[u'target_port'] = "3389"
+ else:
+ kv[u'target_port'] = self.shared.get(u'target_port')
kv[u'session_id'] = str(datetime.now())
kv[u'module'] = 'RDP' if self.shared.get(u'login') != 'internal' else 'INTERNAL'
kv[u'target_password'] = self.shared.get(u'target_password')
With this in place, the procedure goes like this:
passthrough.py
in one terminal shell (with absolute path):
$HOME/src/redemption.git/tools/passthrough/passthrough.py
rdpproxy
in another terminal shell:
rdpproxy -nf
remmina
192.168.RDP.PROXY
in remmina
main bar under "RDP", and hit ENTER
remmina
; since passthrough.py
is hacked to enforce target settings here, you can enter whatever here - my preference these days is type "a" in all fields, and hit ENTER:
remmina
window will likely shut down; the explanation is in the rdpproxy
log:
rdpproxy: INFO (3101423/3101423) -- New Module: MODULE_RDP
rdpproxy: ERR (3101423/3101423) -- OutCryptoTransport::open : open failed (/path/to/extern_disk/rdpproxy/recorded//2023-02-06/07:58-e6565258-cb31-4adf-8034-2a578c4c735e.logred-Y8Mc2h.tmp -> /path/to/extern_disk/rdpproxy/recorded//2023-02-06/07:58-e6565258-cb31-4adf-8034-2a578c4c735e.log): No such file or directory
rdpproxy: [RDP Session] session_id="2023-02-06 07:58:05.611350" client_ip="192.168.RDP.CLIENT" target_ip="209.85.RDP.SERVER.com" user="TARGET_USERNAME" device="209.85.RDP.SERVER.com" service="" account="TARGET_USERNAME" type="SESSION_CREATION_FAILED"
rdpproxy: ERR (3101423/3101423) -- OutCryptoTransport::do_send failed: file not opened (/path/to/extern_disk/rdpproxy/recorded//2023-02-06/07:58-e6565258-cb31-4adf-8034-2a578c4c735e.logred-Y8Mc2h.tmp->)
rdpproxy: INFO (3101423/3101423) -- Socket Authentifier (5) : closing connection
rdpproxy: INFO (3101423/3101423) -- Client Session Disconnected
rdpproxy: [rdpproxy] psid="15482383101423" user="TARGET_USERNAME" type="DISCONNECT"
rdpproxy: INFO (3101423/3101423) -- Socket RDP Client (6) : closing connection
The connection failed, because code wanted to write in subdirectory 2023-02-06
(current day) in /path/to/extern_disk/rdpproxy/recorded
, but the directory does not exist (and code did not create it); thankfully rdpproxy
does not crash because of this, and keeps running; remmina
will however close the window and terminate the session
2023-02-06
) in /path/to/extern_disk/rdpproxy/recorded
and /path/to/extern_disk/rdpproxy/hash
(yes, that one will fail, too), and give them "liberal" permissions (rdpproxy
and passthrough.py
can stay running during this):
mkdir -p /path/to/extern_disk/rdpproxy/{recorded,hash}/2023-02-06
chmod -R a+r,ug+w /path/to/extern_disk/rdpproxy
Repeat the procedure with remmina
: enter "a" in all fields at the "RDP blue screen" and hit ENTER - and ... I didn't experience this yesterday, not sure what changed, but today remmina
terminates the session again, the reason being:
rdpproxy: ERR (3104008/3104008) -- OutCryptoTransport::open: open failed hash file /path/to/extern_disk/rdpproxy/hash//2023-02-06/08:15-0e3a435a-614e-4f86-b41a-48b486717898.log: Permission denied
rdpproxy: INFO (3104008/3104008) -- Socket Authentifier (5) : closing connection
rdpproxy: INFO (3104008/3104008) -- Client Session Disconnected
The weird thing is that the file actually did get created, but with very tight permissions:
$ ls -la /path/to/extern_disk/rdpproxy/hash/2023-02-06/
total 12
drwxrwxr-x 2 user user 4096 Feb 6 08:15 .
drwxrwxrwx 4 user user 4096 Feb 6 08:11 ..
-r--r----- 1 user user 237 Feb 6 08:15 08:15-0e3a435a-614e-4f86-b41a-48b486717898.log
Since it has an UUID in the name which changes, there is no chance we could do chmod
on an active log file after failure and fix it that way; however it seems that this patch to src/transport/crypto_transport.cpp
works:
diff --git a/src/transport/crypto_transport.cpp b/src/transport/crypto_transport.cpp
index 997b6c54e..0255bc940 100644
--- a/src/transport/crypto_transport.cpp
+++ b/src/transport/crypto_transport.cpp
@@ -756,6 +756,7 @@ void OutCryptoTransport::open(const char * finalname, const char * const hash_fi
{
size_t base_len = 0;
const char * base = basename_len(finalname, base_len);
+ file_permissions = FilePermissions(0664);
this->open(finalname, hash_filename, file_permissions, {base, base_len});
}
@@ -808,8 +809,10 @@ void OutCryptoTransport::create_hash_file(HashArray const & qhash, HashArray con
ocrypto hash_encrypter(this->cctx, this->rnd);
OutFileTransport hash_out_file(unique_fd(::open(
this->hash_filename.c_str(),
- O_WRONLY | O_CREAT,
- S_IRUSR | S_IRGRP)));
+ //O_WRONLY | O_CREAT,
+ //S_IRUSR | S_IRGRP)));
+ O_RDWR | O_CREAT,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)));
if (!hash_out_file.is_open()){
int const err = errno;
LOG(LOG_ERR, "OutCryptoTransport::open: open failed hash file %s: %s", this->hash_filename, strerror(err));
remmina
: enter "a" in all fields at the "RDP blue screen" and hit ENTER - and FINALLY we get connection to the target server, and the remote desktop is shown in the remmina
window! And that means, in this case, also recording has started ...Do what you want to do in the remote session, and then disconnect/close the remmina
window; the recording should now stop, and we look for new files in our daily "recorded" folder, in this case /path/to/extern_disk/rdpproxy/recorded/2023-02-06
"
$ ls -la /path/to/extern_disk/rdpproxy/recorded/2023-02-06/*wrm*
-rw-rw-r-- 1 user user 585660 Feb 6 09:20 /path/to/extern_disk/rdpproxy/recorded/2023-02-06/09:19-b4927384-5a46-4eff-b0bb-58c2d818b533-000000.wrm
-rw-rw-r-- 1 user user 350 Feb 6 09:20 /path/to/extern_disk/rdpproxy/recorded/2023-02-06/09:19-b4927384-5a46-4eff-b0bb-58c2d818b533.mwrm
$ cat /path/to/extern_disk/rdpproxy/recorded/2023-02-06/09:19-b4927384-5a46-4eff-b0bb-58c2d818b533.mwrm
v2
1024 768
checksum
/path/to/extern_disk/rdpproxy/recorded//2023-02-06/09:19-b4927384-5a46-4eff-b0bb-58c2d818b533-000000.wrm 585660 33204 1000 1000 2049 14942263 1675671600 1675671600 1675671579 1675671601 1f72d42f9105512c0c6152b8a69892421b8c6717738462af10d3443d3dacb30e ce08419b4f71355972487147c3f77bb09733436430833ca38c7cc6b93bcb4527
We can also note, that the .mwrm
file is a text file, that contains a reference to the .wrm
file.
.(m)wrm
file to .mp4
video file using redrec
. The default settings there feature a compression a bit too heavy for my taste, and also I'd want the settings from Encoding video for the web gist; so my conversion command line is this (note I wanted to specify -an
to disable audio stream, but I couldn't tell how to do that with --video-codec-options
, so I gave if an=1
which should be converted to the incorrect -an=1
, however, it seems to work, as there are no errors or warnings due to this, and there is no autio in the output either):
redrec --encryption=disable --video=on --full=on --frame-rate=25 \
--video-codec-options="pix_fmt=yuv420p profile:v=baseline level=3 an=1" \
-i "/path/to/extern_disk/rdpproxy/recorded/2023-02-06/09:19-b4927384-5a46-4eff-b0bb-58c2d818b533.mwrm" \
-o $PWD/test-000000.mp4
... with the expected command output in terminal for succesful conversion being:
Output file is "$HOME/Desktop/test-000000.mp4". redrec: INFO (3120001/3120001) -- player begin_capture = 0 redrec: INFO (3120001/3120001) -- Enable capture: wrm=no png=no kbd=no video=yes video_full=yes pattern=no ocr=no meta=no
Let's check the resulting files created:
$ ls -la test* -r--r----- 1 user user 2171411 Feb 6 09:44 test-000000-000000.mp4 -rw-rw-r-- 1 user user 2624 Feb 6 09:44 test-000000-000000.png -r--r----- 1 user user 2825884 Feb 6 09:44 test-000000.mp4 -r--r----- 1 user user 37 Feb 6 09:44 test-000000.pgs
We can observe that:
- there are two
mp4
file; one calledtest-000000.mp4
(as specified), the other calledtest-000000-000000.mp4
- We can see that redrec seems to add
-00000x
as suffix to the filename basename, which is likely related to the 10 minute chunks;--full=on
in the options seems to force creation of a singe .mp4 file with a name as specified in command line with-o
argument- (earlier, I used to get permission errors on the .pgs file, but it seems the above patch fixed that) The two mp4 files are slightly different from one another - but we can see that it is the file that has the name as specified on the command line (without suffix) that has the ffmpeg setting that we specified on the command line (also, I had set the client screen resolution to 1024x768 beforehand):
$ for ix in *.mp4; do echo $ix "`ffprobe $ix 2>&1 | grep -A1 '^ Duration:'`"; done test-000000-000000.mp4 Duration: 00:00:20.44, start: 0.760000, bitrate: 849 kb/s Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1024x768, 847 kb/s, 15.85 fps, 25 tbr, 12800 tbn, 50 tbc (default) test-000000.mp4 Duration: 00:00:21.60, start: 0.040000, bitrate: 1046 kb/s Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1024x768, 1043 kb/s, 25.05 fps, 25 tbr, 12800 tbn, 50 tbc (default)
Well, from what I could see, the resulting videos are very decent and responsive - so thanks again for a great software!
Your scheme is good. I didn't know Mermaid, it's nice
For rdpproxy.ini
, anything not listed by --print-default-ini
will be ignored. The ini
variable of the sources is the grouping of the variables read from rdpproxy.ini
and those sent by passthrough.py
. The variables that can be exchanged with the latter are listed in projects/redemption_configs/autogen/doc/sesman_dialog.txt
and the list of all variables is in projects/redemption_configs/configs_specs/config_spec.hpp
(there is a lot of info, but not all of it will be useful to you; such as the one indicating the origin of the option (connpolicy*
)) In this file sesman
= passthrough.py
, ini
= the file rdpproxy.ini, gui
= ini + origin of the option.
Normally you don't need allow_rt_without_recording=1
to have a record. You just need is_rec=1
to be sent by passthrough.py
along with the target address and [video] capture_flags
to be initialized with wrm
. I feel like this is more of an unwelcome side effect.
So, it seems that by default, these settings have break_interval = 600 which is "Time between 2 wrm movies", which as I understanding, means that the video capture will happen in 10 minute chunks (probably to allow for long captures, so you only lose the last 10 minutes if you run out of space on disk).
In reality, even with a space problem, the file can be read down to the last recorded packet and the video generated. But the last second will be missing and the mwrm will have to be completed manually. The split is used so that redrec ignores some files with the --begin
/ --end
options and avoids too large files.
The connection failed, because code wanted to write in subdirectory 2023-02-06 (current day) in /path/to/extern_disk/rdpproxy/recorded
We should be a bit more flexible on this and have the proxy create the folder hierarchy by itself if needed or have passthrough.py do it. Actually, the sub-folder is not a necessity, it is passthrough.py that sends it through the record_filebase
variable.
the problem with the crypto_transport.cpp
step is strange, as you say, the uuid is used to avoid this situation. Historically, the only way to get this kind of error is to get a screen resize (client opens with a certain screen size, server asks for another size), but we've had an option enabled by default for that for a while.
Regarding the redrec options, --video=on
and --full=on
are two independent options. The first one makes a cut based on the detection of window titles (it doesn't always detect them). By setting only --full
, there will be only one file.
I have managed to successfully compile redemption on Ubuntu 20.04 ( see also https://unix.stackexchange.com/questions/734205/dh-install-cannot-find-files-tried-in-debian-buildtmp-but-files-exist-in-debi ), but now I realize, I have no idea how to use it.
What I'm interested in, is capturing my session with a remote PC as video. In the session, I run a couple of graphics/CPU intensive apps, so if I start a screen capture like OBS Studio along with remote desktop (VNC or RDP) everything grinds down to a halt. The remote PC is Windows 10, so RDP is behaving a lot more responsive (as far as I can see desktop decorations etc are removed) even when the intensive applications run, and this is why I want to try capturing the RDP session as video.
I have seen in the README that there are possibilities to capture an RDP session to a video, but I don't get how/what I have to set up, to get a connection to my target server, and capture that session as a video.
Yes, I have seen How to use this project · Issue #25 · wallix/redemption, and I'm still not getting it.
Yes, I have, multiple times - still not getting it.
So, I can see there is a program
rdpproxy
. Just by the naming, I would have imagined, it would have had a local port (maybe 3389), and then I'd specify target address and port - and then I could use my preferred RDP software (remmina) to connect to the proxy, which would have then forwarded/proxied usernames/passwords and traffic between my remmina on my Ubuntu, and the target remote server; and while doing that, it could have captured something that could be converted to video.However, that is not how things work; for one:
...
rdpproxy
has no command options for setting any ports.So, the readme says:
Ok, I did that on the PC, say, 192.168.0.10; and then from 192.168.0.12 I run
remmina
, and intiate RDP connection to 192.168.0.10:3389; I get this:Ok, I did not expect this; what makes this screen,
passthrough.py
orrdpproxy -nf
?In any case, one of those programs creates the screen - so what does that mean, that
rdpproxy
proxies 127.0.0.1 to ... 127.0.0.1?Well, that is all fine as a starting example, however it should have been written in the README what should be expected of this example.
But then, good that I know how the example works now - but how do I use this for my use case? That is, how do I proxy 127.0.0.1 (current machine) to public IP of remote machine, as in example.com:3389? How can I specify a target port that is not 3389 but maybe due to port forwarding it is, say, 13389 (so, example.com:13389)? And how do I capture a video (or rather, as per README, a
.mwrm
or.wrm
log file, which can thereafter be converted to video)?If
rdpproxy
cannot be used directly for this purpose, are there other programs in this project that could be used instead?