Administration – Random IT Utensils https://blog.adamfurmanek.pl IT, operating systems, maths, and more. Wed, 05 Jun 2024 11:06:46 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.2 Bit Twiddling Part 6 — Stop RDP from detaching GUI when the client disconnects https://blog.adamfurmanek.pl/2024/06/05/bit-twiddling-part-6/ https://blog.adamfurmanek.pl/2024/06/05/bit-twiddling-part-6/#respond Wed, 05 Jun 2024 11:06:31 +0000 https://blog.adamfurmanek.pl/?p=5057 Continue reading Bit Twiddling Part 6 — Stop RDP from detaching GUI when the client disconnects]]>

This is the sixth part of the Bit Twiddling series. For your convenience you can find other parts in the table of contents in Par 1 — Modifying Android application on a binary level

Today we’re going to solve the problem when the remote server disconnects GUI when we lock the workstation or disconnect.

Let’s start by explaining the problem a little bit more. Imagine that you connect like this:

Local ---> Remote

If you now lock the Local workstation (with WIN+L) or disconnect, then the GUI on Remote will break. The user on Remote will not be logged out, the applications will continue to work, but the UI will not be there. This effectively breaks things like VNC or apps that take screenshots or click on the screen.

Similarly, the same happens when you connection with a jump host:

Local ---> Windows Server 2019 ---> Remote

If you now disconnect Local or lock Local workstation, then the GUI on Remote will break. However, it’s different if you use Windows Server 2016:

Local ---> Windows Server 2016 ---> Remote

If you now disconnect Local or lock the workstation, then the GUI on Remote will still be there. This suggests that something has changed in Windows Server 2019. This is not surprising as this is the time when Microsoft implemented RemoteFX heavily into their RDP to support cameras, USB redirection, improve security and much more.

At this point we have one solution – just use the Windows Server 2016 jump host and it fixes the issue. However, this way you won’t use the camera (it doesn’t work in 2016 typically) and you need to keep another machine along the way (this could be a virtual machine on your Remote, though). Let’s see if we can do better.

Fixing the workstation lock

The first thing to notice is that if you suspend the mstsc.exe on Local (with Task Manager or debugger) and then lock the workstation, then Remote doesn’t lose the UI. This suggests that disconnecting the GUI is an explicit action of the RDP implementation. However, we can easily fix that.

mstsc.exe registers for session notifications with WTSRegisterSessionNotification. We can see that with ApiMonitor:

We can simply hook this method to not register for any notifications. We can use that with the following WinDBG script:

.sympath srv*C:\tmp*http://msdl.microsoft.com/download/symbols
.reload
bu	WTSAPI32!WTSRegisterSessionNotification	""e @rip 0x48 0xc7 0xc0 0x01 0x00 0x00 0x00 0xC3; qd""
g

The shell code we put here is simply:

move rax, 1
ret

You can run mstsc.exe, attach the debugger, add the breakpoint, resume the application, and then connect to the server. The debugger will detach shortly after you open the connection and you can check that now you can lock the workstation and the GUI will still be there.

Fixing the disconnection

Fixing the case on disconnect is slightly harder. Once again, when we pause the mstsc.exe, then the remote session still works even if we keep mstsc.exe paused for a long time. This suggests that the session is up as long as the connection is alive. Even though we paused the mstsc.exe application, the operating system keeps the connection up on the TCP level. We can exhibit that.

The idea is to run a proxy on the Remote. We’ll then connect like this:

Local ---> Proxy ---> Remote

The proxy must just route the connection to the RDP server (on the port 3389),but do not lose the connection to Remote when the connection from Local disappears. This way we can simply kill the mstsc.exe on Local, and then the connection will still be alive and the GUI will persist. However, if you close mstsc.exe gracefully, then the client will terminate the GUI on the remote server. Therefore, just kill the client. You can also put your machine to sleep and the UI should still work.

]]>
https://blog.adamfurmanek.pl/2024/06/05/bit-twiddling-part-6/feed/ 0
Availability Anywhere Part 27 — Tools for remote work from laptops and XR/VR/AR https://blog.adamfurmanek.pl/2024/05/09/availability-anywhere-part-27/ https://blog.adamfurmanek.pl/2024/05/09/availability-anywhere-part-27/#respond Thu, 09 May 2024 09:54:10 +0000 https://blog.adamfurmanek.pl/?p=5018 Continue reading Availability Anywhere Part 27 — Tools for remote work from laptops and XR/VR/AR]]>

This is the twentieth seventh part of the Availability Anywhere series. For your convenience you can find other parts in the table of contents in Part 1 – Connecting to SSH tunnel automatically in Windows

Last time we discussed how to work remotely. Let’s now look at that from a different perspective and see various tools that we can use and how they work.

Sessions, input, and output

Before seeing the tools let’s understand some basics of how computers work. This will be important to understand what tools we need and how they should work. I’ll be talking mostly from the perspective of Windows OS, but the same applies to other systems.

Whenever you try to use a computer, you need to have a session. Session is an object that controls your human interfaces, most importantly input and output. Depending on the version of Windows, you may be able to create many sessions.

The main session (the one you have when you “sit at a computer”) is called CONSOLE. You can see it with qwinsta command:

>qwinsta
 SESSIONNAME       USERNAME                 ID  STATE   TYPE        DEVICE
 services                                    0  Disc
 console                                     1  Conn

CONSOLE is what you create when you use your computer “normally”. This session handles your keyboard, mouse, and (most importantly) your monitors.

You can create more sessions. Whenever you use mstsc.exe, you connect to a computer and create a session:

SESSIONNAME       USERNAME                 ID  STATE   TYPE        DEVICE
 rdp-tcp#112       user                     2  Active

Obviously, when you create a new session, it must have some way of getting input (mouse and keyboard) and producing output (monitors).

It’s important to understand that one physical monitor can be in one session only (at least I have no idea how to use it in many sessions and I don’t think that would make sense). After all, you just have “one picture” on the monitor and there is no point in showing many sessions in it. Sure, you could fake it to some extent maybe to do picture-in-picture, but I really don’t see a decent use case for that.

While all of that sounds super clear, good understanding is crucial for picking the right tools.

What do we really need

Let’s now think what we really need to do the remote work.

Regarding sessions:

  • Do we want to connect to an existing session? In other words, do we want to connect to the state that the “person sitting next to the computer” sees?
  • Do we want to create a new session? This will start things from scratch just like we “restarted” the computer

However, the more important question we need to consider is how do we want to interact with the session?

  • Do we want to control it at the destination (“remote”)?
  • Do we want to control it from both places (“remote” and “local”)? This is “shared” control.
  • Or maybe only “local” should control it? This is “takeover” control.
  • Do you want the keyboard shortcuts pressed on “local” to be sent to “remote”?

If you read this carefully, you may realize some interesting facts that may change the way you look for the remote work software:

  • If you want to create a session and control it from “local” then you need RDP-like (mstsc.exe-like) application
  • If you want to connect to the existing session and control it from “local” then you need VNC-like software
  • If you want to connect to the existing session and control it from “remote” then you basically need a screen share

While all these things may seem rather trivial, they have crucial impact on what we’re actually trying to achieve. Let’s see the typical scenarios:

  • You want to connect to a remote server (like VDI, Azure VM, etc.) – in that case you want to create a new session and you want to control it from “local” which is RDP-like software
  • You want to help someone else with their computer – in that case you connect to the existing session and you want to let “remote” control it which is a screen share
  • You want to play a game on your VR goggles from your PVCR – in that case you connect to the existing session and you want to control it from “local” or from both places which is a VNC-like software
  • You want to use VR goggles to work from your laptop – in that case you connect to the existing session and you want to let “remote” control it which is a screen share

Notice that a regular “monitor” can be thought of a remote connection to the existing session that is controlled from “remote” so it’s basically a screen share. However, you could have a TV that you use to connect to the existing session and control it from “local” with your remote control (no pun intended) which is a VNC-like software.

Multiple monitors

Now we can ask the question of the monitors. This is crucial as here are many ways we can configure our machines.
Let’s assume that our “local” has 3 screens and our “remote” has 2 physical screens. So the geometry in “remote” may look like (1920, 1080, 0, 0) and (1920, 1080, 1920, 0) (which is (width, height, left, top)). Similarly, geometry in “local” may be (1920, 1080, 0, 0) + (1920, 1080, -1920, 0) + (1920, 1080, 1920, 1080).

Let’s consider two scenarios.

Creating new session in remote

If we create a new session in remote then we need to provide monitors for it. Effectively, it doesn’t matter what monitors are there in “remote” because we won’t use them. The only important thing that matters here is that we have 3 monitors in “local”. To understand what’s going on, we now need to decide on how many virtual monitors we want to create in “remote” and how we want to map them in “local”. There are typically the following options:

  • 1:1 – You create one monitor in “remote” and you map it to one monitor in “local” – this is how most RDP clients work
  • 1:n – You create one monitor in “remote” and you show it on multiple monitors in “local” – this is how xfreerdp workd for me. Sometimes this is called “span”
  • n:n – You create as many monitors in “remote” as you have in “local” – this is how mstsc.exe works when you select “use all displays”. Sometimes this is called “multimon”
  • m:m – You create fewer monitors in “remote” then you have in “local” but each monitor is mapped to one monitor. This is how mstsc.exe works when you modify the .rdp file directly
  • m:n – You create some monitors in “remote” and you map them to some configuration in “local”. I don’t know any software that would do this. Notice that this doesn’t mean that you use entire “local” monitor. You could come up with any geometry that you need.

Whenever I talk about “multiple monitors” I mean the n:n or m:m scenario from the above.

Connecting to he existing session in remote

In this scenario the “remote” has 2 monitors. You now want to map them somehow in your machine. You have the following options:

  • 1:1 – You show only one “remote” monitor in one “local”
  • m:1 – You show all “remote” monitors in one “local” (typically one window) – this is how most VNC apps work
  • m:m – You show “remote” monitors separately in “local”. This rarely works on Windows
  • m:n – You configure custom geometry. I’m not aware of any software that does that

Let’s now see the software for various scenarios.

Software

Applications that create new session (RDP-like) (and therefore “local” must control the session):

I’m not aware of an RDP-like application that would work in VR goggles.

VNC-like applications:

  • TightVNC, TigerVNC, UltraVNC and similar – they typically support 1:1 and “shared” control. You can often change the geometry on the server (I didn’t see any client that would let me change the geometry, though).
  • vSpatial – this supports n:n, and “shared” or “takeover”
  • noVNC – this is a web VNC client. It supports 1:1 and “shared”. It’s just an HTML file with some JS, so you can easily adjust it to your needs
  • Immersed – this supports n:n and “shared”
  • Virtual Desktop, Multirooms – they support 1:1 and “shared”

Screen share applications:

  • vdo.ninja – this shares one screen and works in browser, so you can share as many screens as you need
  • Google Meets, Teams, Amazon Chime, Zoom, Webex and many others – they share one screen and work in browser or in dedicated apps. You can open up many bridges to share many screens

And now the most important part (kind of TL;DR):

  • You want to have the “true” RDP experience with multiple monitors – mstsc.exe or Thincast Remote Desktop Client
  • You want to interact with some remote session from your computer – any VNC client that works for your case (and your mileage may vary significantly) – TightVNC, UltraVNC, TigerVNC, noVNC, many more. Also, vSpatial
  • You just want to see the remote session only – any screen share or VNC. Specifically, you don’t need Immersed or other “built-in” VR apps that stream the screen. You can just open up a browser and use vdo.ninja!

Being that said. If you just want to use your goggles as a “monitor” for your laptop, then you don’t need any CPU/GPU-heavy applications for that. Just share the screen in browser and it works.

And if you want to offload that even more, then use RDP + Screen share:

  • Take your road runner and connect over RDP to a remote machine. This way you can create a session there with monitors (and you can use IndirectDisplayDriver or BetterDisplay to create virtual monitors)
  • Use your VR goggles to connect to see the screen shared from the remote machine (with screen share solutions like browser, VNC, vSpatial or whatever else)

This way your road runner does nearly nothing. It just connects to the remote machine. Similarly, your VR goggles don’t work hard. They just run a browser or something light.

]]>
https://blog.adamfurmanek.pl/2024/05/09/availability-anywhere-part-27/feed/ 0
Availability Anywhere Part 26 — Working remotely like a pro https://blog.adamfurmanek.pl/2024/04/24/availability-anywhere-part-26/ https://blog.adamfurmanek.pl/2024/04/24/availability-anywhere-part-26/#respond Wed, 24 Apr 2024 19:59:55 +0000 https://blog.adamfurmanek.pl/?p=5004 Continue reading Availability Anywhere Part 26 — Working remotely like a pro]]>

This is the twentieth sixth part of the Availability Anywhere series. For your convenience you can find other parts in the table of contents in Part 1 – Connecting to SSH tunnel automatically in Windows

Over the years I improved my remote work setup significantly. This blog post summarizes what various approaches I took and how I configured my setup over the years. One note before moving on – I assume you don’t have any “hardware” but the computer (so no programmable boards, weird mobile devices, confidential hardware etc.).

Level 1

First approach is what most of us consider “remote work”. You just take your corporate laptop with you and you work remotely. You probably have a corporate VPN that lets you connect to the company’s resources from wherever you are.

Pros:

  • You can do everything the same way you did while in the office

Cons:

  • You can’t work from devices that are not onboarded with your corporate infrastructure – mobile phones, tablets, private laptops, VR goggles, etc.
  • You need to take the corporate laptop with you anywhere you go
  • You work from a device that is probably hot and loud working crazy on your office stuff (we all know the bloatware they install on the machines…)
  • You can rarely pick your hardware, so your laptop is probably big and bulky (and yes, Macbook Air is big and bulky)
  • You may not be able to connect to the VPN in some places due to weird networking conditions

While this is enough to actually do the work, I don’t like this approach. First, I always want to be able to be online (that doesn’t mean that I’m online all the time) no matter where I am and what I have on me. Specifically, I want to be able to access the infrastructure and do all my stuff using my mobile phone, private laptop, etc. I mean, I want to be able to do coding or video processing from my mobile phone while on a train (yes, I did that).

Second, I don’t like taking my corporate machine with me. This is especially troublesome when I need to travel for conferences for which I need to take more hardware with me (like 2 laptops and many mobile phones). Taking another laptop on the plane is just cumbersome. This is sometimes even dangerous depending on where you go and when they want to control your corporate machines. Not to mention, that your corporate policy may prohibit you from taking the corporate laptop to some places.

Third, I don’t like when I can’t pick my hardware. My laptop must be light and quiet, especially when I sit in a deckchair or lie down.

Level 1.5

Sometimes you cannot connect to the VPN while working remotely. This is due to weird networking, policy, or simply geolocation block.

You can fix that by getting a networking device that will connect to some other VPN and expose a tunnel for you. Typical solution is to get a router with built-in VPN. You can see this tutorial to learn how to do that.

However, I don’t recommend taking a router for that. Just get yourself a decent Windows x86 tablet, for instance Dell Venue 8 Pro and install SoftEther VPN. First, such a tablet is a “full Windows machine” so you can do whatever is needed to get connected (think about captive portals or weird passwords). Second, since this is a tablet, you can take it with you wherever you need.

Level 2

Let’s not bring the corporate machine with us at all. Get yourself a corporate virtual machine (typically called virtual desktop or VDI) and do all the work from it. Next, to work remotely, you just need to connect to the VDI from your device (which we’ll call road runner) using RDP + SSH. To do that, you may need to onboard your road runner with your corporate VPN. However, there are many solutions to work that around, so from now on we assume your road runner is not onboarded with the corporate bloatware.

Pros:

  • You can do almost everything the same way you did while in the office
  • You can work from your laptop
  • You can take your hardware anywhere you wish

Cons:

  • You may still be unable to work from mobile phones, tablets, VR goggles, etc.
  • You may need to run video conferencing software on your road runner (which may not be possible)
  • Your road runner may still get hot and loud when taking crazy video call with greenscreen effects
  • You may not be able to do some corporate tasks – think about GPU-heavy things like video processing

This is much better now since we can connect from our road runner, so we can get a light laptop like Acer Swift 5 which is 1kg/2.2lb (your Macbook Air is at least 25% heavier).

When it comes to video conferences, they may be tricky. First, your webcam may not work over RDP. Second, RDP introduces noticeable audio delay. To fix that, you may need to run the conference software locally (I recommend doing that in browser). While it solves the problem, it also makes your road runner loud and hot.

Last but not least, if your company cannot prepare a VDI for you, you can create it on your own. Just use Sysinternal’s disk2vhd. Keep in mind it may cause some troubles with Active Domain and IdP in general, but it works.

Level 3

Let’s finally address the problem that we can’t work from any device. Let’s add a jump host. Get yourself a decent Windows machine and put it somewhere on the Internet. I generally recommend Hetzner (but whatever you choose, just get yourself a dedicated host). And BTW, choose Intel CPU instead of AMD (your virtual machines will thank you for that). Next, connect to that machine with plain RDP. This way, you can connect to it using any road runner – be it laptop, mobile phone, tablet, or whatever else. And since it can be a mobile phone, just get yourself a decent foldable. Yes, that really makes a difference when you’re working remotely.

Pros:

  • You can do almost everything the same way you did while in the office
  • You can work from nearly any device
  • You can take your hardware anywhere you wish

Cons:

  • You may need to run video conferencing software on your road runner (which may not be possible)
  • Your road runner may still get hot and loud when taking crazy video call with greenscreen effects
  • You may not be able to do some corporate tasks – think about GPU-heavy things like video processing
  • You may have hard time connecting to the corporate VPN

When dealing with corporate VPN, use things like SSLH or FileProxy for avoiding VPN without split tunneling (also known as TCP over File System). There are many more tricks and improvements you can apply. I cover them in my talk.

Level 4

Let’s now deal with more devices and the GPU. I use two solutions for that: vSpatial and NoMachine.

First, vSpatial lets you connect to your jump host with browser and application for VR goggles. This way, you can work from basically any device you can think of. Another bonus is that vSpatial lets you forward audio and camera, so you can take your meetings from the jump host. However, as of today, vSpatial supports only 20 FPS for your webcam, so people will notice that something is off. We’ll deal with that in a second.

Second, NoMachine lets you connect to the jump host over VNC-like protocol which lets you use GPU easily. You can now do basically anything you can think of.

Pros:

  • You can do everything the same way you did while in the office (assuming your VDI can do the things your corporate laptop could)
  • You can work from any device
  • You can take your hardware anywhere you wish

Cons:

  • You may need to run video conferencing software on your road runner (which may not be possible)
  • Your road runner may still get hot and loud when taking crazy video call with greenscreen effects
  • You may have hard time connecting to the corporate VPN

Nothing stops you from having more jump hosts. For instance, you can host a dedicated Mac mini in Cyberlynk and connect to it.

Also, if you need more screens with your road runner, you can try portable monitor. You can also emulate screens with IddSampleDriver or its Rust version, or Better Display. This way you can literally move between machines with not a single window changing its position.

Level 5

Let’s finally deal with our road runners becoming loud and hot. We basically want to offload whole video processing to the jump host so that our device doesn’t need to encode/decode any video conferencing stuff.

To do that, use VDO.Ninja. This lets you “forward” your audio and camera to any jump host. Just use OBS or Splitcam on the jumphost to receive the video, apply any greenscreen effects you need, and forward it to the conference call.

Pros:

  • You can do everything the same way you did while in the office (assuming your VDI can do the things your corporate laptop could)
  • You can work from any device
  • You can take your hardware anywhere you wish
  • Your hardware stays cool and quiet no matter what you do

Cons:

  • You may have hard time connecting to the corporate VPN

This is how my final setup looks like. I don’t do anything on my road runner. Nothing. I just connect to the jump host with RDP + vSpatial + NoMachine and I forward my audio + video with VDO.Ninja. No matter if I code, process videos, record stuff, attend conference call (or many of them), remove my webcam background, or anything else – my road runner has constant and predictable load. I can also switch to any other road runner easily.

Double-thin-client architecture

Yet another trick you can apply is what I call “double-thin-client” architecture. Instead of booting up your road runner natively, you can use native boot and get yourself a portable SSD drive like Samsung T7.

This way, you can install your operating system once, and carry it with you wherever you go. You just plug the drive to the USB, boot the operating system from the drive, and you have your environment up and running. You can then replace your road runner with another machine easily. I actually did that many times. My Windows installation survived like 4 different laptops already. Best thing is, you can simply clone the drive to another one when you get a new SSD, so you can upgrade your hardware in place with no failures.

You can use similar approach for your jump host. Do not install things locally. Just boot from VHD and this way you can easily take backup of your dedicated host.

Other improvements

There are many more things you can consider for improving your remote work. Some ideas include Bluetooth devices (obviously) and VNC server installed on a road runner, so you can walk around your home and control the device from your mobile phone. This can be useful when you’re already dialed into a call and you don’t want to rejoin from another device. Simply take your Bluetooth headset with you to another room and watch the screen of your road runner from a mobile phone.

Another idea is automatically capturing meeting notes. Since you take calls on your jump host, you can run some notetaker AI application that will automatically capture whatever you need and send it forward.

You can use Firefox with Multi-Account Containers and Tree Style Tab. They really make your work way faster. Too bad it’s just for Firefox.

You may also want to use VirtuaWin to have many desktops. I find it much better to use than “tasks” in Windows, mostly thanks to keyboard shortcuts. And if you need to run applications that need to actively interact with your desktop (like Puppeteer or UIPath), just use Desktops.

Tricks for mobile phones

You can also work on your mobile phone. First, you can get a silicone keyboard to type easily. This is really great and you can actually work from your mobile phone. I once travelled for two days with my mobile phone only and I was still able to do coding and debugging.

You can dial in over GSM. This may simplify the way you take online meetings.

Next, you can run many clones of one application with Multiple Accounts. This way, you can separate your corporate stuff from your personal one on your road runner.

You can virtualize mobile phone inside a mobile phone. Just consider applications like VMOS or Virtual Master – Android Clone. You can try Limbo x86 PC Emulator.

You can turn your mobile phone into a mobile workstation with Samsung DeX.

Short note on Apple devices

I sometimes get asked why I don’t use Apple devices. If you watch my conference recording, you can see that I always use Windows and Android. This is surprising to some people because they consider me a “power user” and yet it seems like I don’t use Apple devices. Let me give you some reasons.

First, I do use them. I really like my Mac with silicon chip as it’s really great CPU + GPU in one box. However, I don’t use it as a road runner for these reasons:

  • It’s heavy. It’s over 25% heavier than my Windows road runner. While it’s not a big deal as it’s still relatively light, I just don’t want to carry additional kilograms
  • It is loud and hot. I don’t like that it gets so hot when I typical stuff. This is especially irritating when working in bed
  • It doesn’t support booting from VHD which I need for my public speaking stuff
  • Most importantly, Macs do not support many screens easily. Right now, I have 5 displays on my desk (not counting the monitor one). When I travel, I often plug additional screens to my laptop. I just don’t accept that Macbook Air doesn’t handle 3 external screens

When it comes to iPhone, I have these reasons to prefer Android:

  • iPhone doesn’t record calls. This is a major obstacle for which there is no easy workaround
  • iPhone can’t run many instances of an app. I want to run them independently because I need to log in with multiple accounts. Some applications support that natively, but I need a generic solution
  • Foldable phone is really a game changer
  • I have some “sophisticated” applications running on my mobile phone, for instance web server and Node.js applications that automate things around text messages. I simply can’t have that on iPhone
  • Many things are not available for iPhone, like some of my VPNs, port forwarding, etc. Android just works better

That’s it. I’ve been using iPhone for some time and it just doesn’t cut. For Macbook, I may use it as a road runner one day when it improves the hardware specification (sic!).

]]>
https://blog.adamfurmanek.pl/2024/04/24/availability-anywhere-part-26/feed/ 0
Availability Anywhere Part 23 — RDP over VR goggles with no PCVR https://blog.adamfurmanek.pl/2024/02/21/availability-anywhere-part-23/ https://blog.adamfurmanek.pl/2024/02/21/availability-anywhere-part-23/#respond Wed, 21 Feb 2024 21:10:54 +0000 https://blog.adamfurmanek.pl/?p=4961 Continue reading Availability Anywhere Part 23 — RDP over VR goggles with no PCVR]]>

This is the twentieth third part of the Availability Anywhere series. For your convenience you can find other parts in the table of contents in Part 1 – Connecting to SSH tunnel automatically in Windows

In this part we’re going to see how we can work remotely from VR goggles with no PCVR around. Effectively, we can work remotely with goggles and a bluetooth keyboard, nothing else.

My workstation

My home workstation is quite powerful. I have 5 physical screens which I use to display 4 different screens. I also use VirtuaWin to have many desktops. I also work remotely from a dedicated machine to which I connect using MS RDP (mstsc.exe).

My typical setup works like this:

  • My main physical screen is 24” with FullHD resolution. I duplicate this physical screen to another physical screen which is 10” and has touch capabilities. I use the touch to draw diagrams.
  • My two additional side screens are 32” with 4K resolution
  • My fifth screen is 10” big and I use it to display all the instant messengers I need (slacks, discords, meets etc). That’s basically a browser window that I configure to be visible on all of the desktops.
  • I have 11 desktops configured in VirtuaWin.
  • To connect remotely to my machine, I move to some other VirtuaWin desktop and use four physical screens (FullHD + 2x4K + touch-enabled screen) to open mstsc.exe.
  • When I need to connect to some other machine, I basically do CTRL+ALT+HOME to unfocus the current RDP, then I switch to another VirtuaWin desktop, and there I connect to the server. This way I can quickly switch between multiple machines I have

This setup works great for me. I can work remotely from any place around the planet. When I’m not at home, I can use my road runner laptop with two physical screens or my mobile phone to connect over RDP to the remote server. I do various things over RDP – I code, I write/read documents, I postprocess videos, I record videos (yes, I record them over RDP!), I browse the Internet. Typical office work.

I wanted to replicate this setup as much as possible with VR goggles. I managed to do that with Meta Quest 3 and vSpatial. Let’s see how I did that.

What we need

First, you need the goggles. I used Meta Quest 3 and they were quite good. I can’t complain on the image quality and I definitely can do all the stuff I need. The battery lasts for around 90-120 minutes which is okayish.

Next, you need to have a machine with many physical displays. That’s the most tricky part if you want to configure it entirely over RDP and use free version of vSpatial. I use two dedicated machines: one is Windows Server and the other is Mac. I first connect to Windows Server from my mobile phone over the regular RDP and log in as User1. At this point, I have a session in Windows Server that has just one display. Next, I use NoMachine to connect from Windows Server to my Mac. In my Mac, I create multiple physical screens using Better Display. Next, I connect from my Mac to my Windows Server and log in as User2 using the regular RDP application. At this point, I have a session in Windows Server that has multiple physical screens. All of that done entirely from my mobile phone, so I can do that when I’m travelling. While it works and allows for any setup of monitors, it’s probably good enough to just use 4 screens from paid vSpatial. Up to you.

Next, we need the vSpatial server. I install it in Windows Server for User2. I then log in to Windows Server using vSpatial application in Meta Quest 3 goggles. The vSpatial client correctly recognizes all 4 displays and now I have access to a regular Windows machine. I can install VirtuaWin and configure desktops the regular way. It all works just like on the screenshot below:

You can see one screen at the top and three screens in the middle. This is nearly the same setup as I have at home.

Finally, I just pair my bluetooth keyboard with touchpad and that’s it. CTRL+ALT+HOME works, so I can work the same way as at home.

Now, sky is the limit. Since I managed to RDP into a regular Windows machine, I can RDP anywhere I need, I can install any application I need, and I can do all of that using my goggles and the bluetooth keyboard only. I want to stress that there is no PCVR here. The Windows Machine with vSpatial server is completely virtualized and has no dedicated graphics card. Also, I don’t use powerful Internet connection. I just connect over regular WiFi. This works from mobile hotspot as well as it’s just RDP.

When it comes to online meetings, I can take them from my mobile phone. If I want to share the screen, I can just join the same meeting from my mobile phone and from my remote machine, and I can share the screen from Windows Server. However, you can also start browser on VR and join meeting like in Google Meet. The meeting will work even after you switch to vSpatial.

Quirks

There are quirks, obviously.

First, Meta Quest 3 seems to be based on Android. Bluetooth keyboards don’t work 100% properly with Android when you RDP to Windows. One issue is that the WIN key doesn’t work. Another, some shortcuts (like ALT+TAB) are captured by the host (goggles in this case).

To solve the lack of WIN key, I use PowerToys. I just remap CAPS LOCK to LEFT WIN on my Windows Server. You can also use AutoHotkey with the following script:

*CapsLock::
    Send {Blind}{LWin Down}
Return

*CapsLock up::
    Send {Blind}{LWin Up}
Return

The script rebinds CAPS LOCK to LEFT WIN. No matter which method you use, you can just use CAPS LOCK in all the shortcuts (like maximizing the window with LEFT WIN+ARROW UP which becomes CAPS LOCK+ARROW UP). While it’s a bit painful, it works. You need to install AutoHotkey in all the remote machines you connect to.

I didn’t solve the ALT+TAB issue with AutoHotkey. I solved it with PowerToys by remapping SCROLL LOCK to TAB. I think you could use AutoHotkey to remap it as well.

Second, vSpatial has a concept of active (focused) desktop. If you worked on one screen and want to click on another one, the mouse click changes the focus. You need to click again to do the actual click you want. This is not a big deal and I got used to it very fast.

Third, I think vSpatial is slightly slower than the regular RDP. I don’t find this as a problem, though, as it’s fast enough for me.

Fourth, the goggles don’t last for long (up to two hours). On the other hand, maybe it’s better for your eyes. You can also buy an additional strap with built-it powerbank.

Summary

It took me quite a while to figure out how to configure this setup. However, now I can truly work using goggles and no laptop around. That sounds like a great approach when traveling – imagine sitting at the airport and having multiple screens, just like at home. Now, the only issue is around the goggles. I hope one day they will be smaller, lighter, and will have stronger batteries. You can always plug them into powerbank if needed, though.

]]>
https://blog.adamfurmanek.pl/2024/02/21/availability-anywhere-part-23/feed/ 0
Availability Anywhere Part 22 — Customer Experience Improvement Program restarts https://blog.adamfurmanek.pl/2024/02/11/availability-anywhere-part-22/ https://blog.adamfurmanek.pl/2024/02/11/availability-anywhere-part-22/#respond Sun, 11 Feb 2024 10:10:10 +0000 https://blog.adamfurmanek.pl/?p=4943 Continue reading Availability Anywhere Part 22 — Customer Experience Improvement Program restarts]]>

This is the twentieth second part of the Availability Anywhere series. For your convenience you can find other parts in the table of contents in Part 1 – Connecting to SSH tunnel automatically in Windows

I was recently investigating a case of a computer restarting every Sunday at 3AM UTC. I couldn’t figure out what was going on and I suspected Customer Experience Improvement Program as I found the following event in event viewer:

User Logoff Notification for Customer Experience Improvement Program

- <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
- <System>
		<Provider Name="User32" Guid="{b0aa8734-56f7-41cc-b2f4-de228e98b946}" EventSourceName="User32" /> 
		<EventID Qualifiers="32768">1074</EventID> 
		<Version>0</Version> 
		<Level>4</Level> 
		<Task>0</Task> 
		<Opcode>0</Opcode> 
		<Keywords>0x8080000000000000</Keywords> 
		<TimeCreated SystemTime="2024-02-04T03:00:01.2906001Z" /> 
		<EventRecordID>404828</EventRecordID> 
		<Correlation /> 
		<Execution ProcessID="896" ThreadID="804" /> 
		<Channel>System</Channel> 
		<Computer>computerName</Computer> 
		<Security UserID="S-1-5-18" /> 
	</System>
- <EventData>
		<Data Name="param1">C:\Windows\system32\shutdown.EXE (computerName)</Data> 
		<Data Name="param2">computerName</Data> 
		<Data Name="param3">No title for this reason could be found</Data> 
		<Data Name="param4">0x800000ff</Data> 
		<Data Name="param5">restart</Data> 
		<Data Name="param6">System is scheduled to reboot in 10 minutes. Please save your work.</Data> 
		<Data Name="param7">NT AUTHORITY\SYSTEM</Data> 
	</EventData>
</Event>

I checked many solutions on the Internet and none of them helped. However, later I realized it was not due to Customer Experience Improvement Program. After carefully checking the event viewer, I found the following:

The process C:\Windows\system32\shutdown.EXE (computerName) has initiated the restart of computer computerName on behalf of user NT AUTHORITY\SYSTEM for the following reason: No title for this reason could be found
Reason Code: 0x800000ff
Shutdown Type: restart
Comment: System is scheduled to reboot in 10 minutes. Please save your work.

- <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
- <System>
		<Provider Name="Microsoft-Windows-Winlogon" Guid="{dbe9b383-7cf3-4331-91cc-a3cb16a3b538}" /> 
		<EventID>7002</EventID> 
		<Version>0</Version> 
		<Level>4</Level> 
		<Task>1102</Task> 
		<Opcode>0</Opcode> 
		<Keywords>0x2000200000000000</Keywords> 
		<TimeCreated SystemTime="2024-02-04T03:10:41.0539456Z" /> 
		<EventRecordID>404868</EventRecordID> 
		<Correlation /> 
		<Execution ProcessID="6028" ThreadID="6892" /> 
		<Channel>System</Channel> 
		<Computer>computerName</Computer> 
		<Security UserID="S-1-5-18" /> 
	</System>
- <EventData>
		<Data Name="TSId">2</Data> 
		<Data Name="UserSid">S-1-5-21-1801674531-515967899-839522115-19733726</Data> 
	</EventData>
</Event>

So it seems that something triggered the restart. I wasn’t sure what that was and I just solved it by cancelling the restart. Just run this batch script to cancel any shutdowns every 30 seconds:

:start
shutdown /a
timeout 30
goto start

It worked good enough.

]]>
https://blog.adamfurmanek.pl/2024/02/11/availability-anywhere-part-22/feed/ 0
Availability Anywhere Part 21 — Fixed mstsc.exe broken UI https://blog.adamfurmanek.pl/2023/12/16/availability-anywhere-part-21/ https://blog.adamfurmanek.pl/2023/12/16/availability-anywhere-part-21/#respond Sat, 16 Dec 2023 07:32:14 +0000 https://blog.adamfurmanek.pl/?p=4922 Continue reading Availability Anywhere Part 21 — Fixed mstsc.exe broken UI]]>

This is the twentieth first part of the Availability Anywhere series. For your convenience you can find other parts in the table of contents in Part 1 – Connecting to SSH tunnel automatically in Windows

I recently installed a set of November 23′ updates for Windows 10 and mstsc.exe disappeared! That was definitely unexpected and I couldn’t find a solution on the Internet. I decided to copy mstsc.exe from some other machine and it wasn’t as simple as possible.

TL;DR; Those are the files that you may need:

  • C:\Windows\System32\mstsc.exe
  • C:\Windows\System32\mstscax.dll
  • C:\Windows\System32\en-US\mstsc.exe.mui
  • C:\Windows\System32\en-US\mstscax.dll.mui
  • C:\Windows\System32\wbem\en-US\mstsc.mfl
  • C:\Windows\System32\wbem\en-US\mstscax.mfl
  • C:\Windows\SystemResources\mstsc.exe.mun
  • C:\Windows\SystemResources\mstscax.dll.mun

You may need similar files from C:\Windows\SysWOW64 or for different locales like pl-PL.

When you skip files in SystemResources, then your mstsc.exe will look like this (attaching this image so Google can index it and people have easier time finding things like “mstsc.exe no options” or “mstsc.exe UI broken” or “mstsc.exe no settings” or “mstsc.exe no show options”):

Copying files into C:\Windows\SystemResources is not as simple as it may seem. That directory is owned by TrustedInstaller and even SYSTEM has no permissions to write there. You can run shell like in this StackOverflow answer which I copy here just in case (run this in PowerShell started as Administrator):

Install-Module -Name NtObjectManager
Start-Service -Name TrustedInstaller
$parent = Get-NtProcess -ServiceName TrustedInstaller
$proc = New-Win32Process cmd.exe -CreationFlags NewConsole -ParentProcess $parent

Even though whoami shows nt authority\system, this effectively runs as TrustedInstaller.

If you wonder how I found out about these files, I used ProcessMonitor and looked for CreateFile operation that failed.

]]>
https://blog.adamfurmanek.pl/2023/12/16/availability-anywhere-part-21/feed/ 0
Availability Anywhere Part 19 — Banning RDP and SSH attacks https://blog.adamfurmanek.pl/2023/01/13/availability-anywhere-part-19/ https://blog.adamfurmanek.pl/2023/01/13/availability-anywhere-part-19/#respond Fri, 13 Jan 2023 09:00:05 +0000 https://blog.adamfurmanek.pl/?p=4821 Continue reading Availability Anywhere Part 19 — Banning RDP and SSH attacks]]>

This is the nineteenth part of the Availability Anywhere series. For your convenience you can find other parts in the table of contents in Part 1 – Connecting to SSH tunnel automatically in Windows

If you expose RDP or OpenSSH to the wide Internet, you’ll most likely get automated attacks. There is a way to block these attacks with firewall, but I didn’t find a nice solution to do so, so I created my own.

The idea is as follows: we periodically scan event log to get failed authentication attempts. We extract the IP address, and then ban it if it happened too often.

Event for RDP is in Security with ID 4625:

- <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
- <System>
  <Provider Name="Microsoft-Windows-Security-Auditing" Guid="{54849625-5478-4994-A5BA-3E3B0328C30D}" /> 
  <EventID>4625</EventID> 
  <Version>0</Version> 
  <Level>0</Level> 
  <Task>12544</Task> 
  <Opcode>0</Opcode> 
  <Keywords>0x8010000000000000</Keywords> 
  <TimeCreated SystemTime="2023-07-15T08:12:14.780596300Z" /> 
  <EventRecordID>27785286</EventRecordID> 
  <Correlation /> 
  <Execution ProcessID="992" ThreadID="41708" /> 
  <Channel>Security</Channel> 
  <Computer>COMPUTER</Computer> 
  <Security /> 
  </System>
- <EventData>
  <Data Name="SubjectUserSid">S-1-0-0</Data> 
  <Data Name="SubjectUserName">-</Data> 
  <Data Name="SubjectDomainName">-</Data> 
  <Data Name="SubjectLogonId">0x0</Data> 
  <Data Name="TargetUserSid">S-1-0-0</Data> 
  <Data Name="TargetUserName">administrator</Data> 
  <Data Name="TargetDomainName">domainname</Data> 
  <Data Name="Status">0xc000006d</Data> 
  <Data Name="FailureReason">%%2313</Data> 
  <Data Name="SubStatus">0xc000006a</Data> 
  <Data Name="LogonType">3</Data> 
  <Data Name="LogonProcessName">NtLmSsp</Data> 
  <Data Name="AuthenticationPackageName">NTLM</Data> 
  <Data Name="WorkstationName">LOCALPCNAME</Data> 
  <Data Name="TransmittedServices">-</Data> 
  <Data Name="LmPackageName">-</Data> 
  <Data Name="KeyLength">0</Data> 
  <Data Name="ProcessId">0x0</Data> 
  <Data Name="ProcessName">-</Data> 
  <Data Name="IpAddress">ADDRESS</Data> 
  <Data Name="IpPort">PORT</Data> 
  </EventData>
  </Event>

For OpenSSH we extract these two events: Applications and Services -> OpenSSH -> Admin for IDs 1 and 2:

- <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
- <System>
  <Provider Name="OpenSSH" Guid="{C4B57D35-0636-4BC3-A262-370F249F9802}" /> 
  <EventID>1</EventID> 
  <Version>0</Version> 
  <Level>1</Level> 
  <Task>0</Task> 
  <Opcode>0</Opcode> 
  <Keywords>0x8000000000000000</Keywords> 
  <TimeCreated SystemTime="2023-07-14T17:28:51.261109200Z" /> 
  <EventRecordID>46166</EventRecordID> 
  <Correlation /> 
  <Execution ProcessID="32716" ThreadID="32356" /> 
  <Channel>OpenSSH/Admin</Channel> 
  <Computer>COMPUTER</Computer> 
  <Security UserID="S-1-5-18" /> 
  </System>
- <EventData>
  <Data Name="process">sshd</Data> 
  <Data Name="payload">fatal: Timeout before authentication for ADDRESS port PORT</Data> 
  </EventData>
  </Event>

and

- <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
- <System>
  <Provider Name="OpenSSH" Guid="{C4B57D35-0636-4BC3-A262-370F249F9802}" /> 
  <EventID>2</EventID> 
  <Version>0</Version> 
  <Level>2</Level> 
  <Task>0</Task> 
  <Opcode>0</Opcode> 
  <Keywords>0x8000000000000000</Keywords> 
  <TimeCreated SystemTime="2023-07-13T07:17:18.615649100Z" /> 
  <EventRecordID>45523</EventRecordID> 
  <Correlation /> 
  <Execution ProcessID="12388" ThreadID="24004" /> 
  <Channel>OpenSSH/Admin</Channel> 
  <Computer>COMPUTER</Computer> 
  <Security UserID="S-1-5-18" /> 
  </System>
- <EventData>
  <Data Name="process">sshd</Data> 
  <Data Name="payload">error: maximum authentication attempts exceeded for invalid user root from ADDRESS port PORT ssh2 [preauth]</Data> 
  </EventData>
  </Event>

We ban them with the following:

while($True){	
	write-host (Get-Date)
	$timeFrame = (Get-Date).AddMinutes(-15)
	$attackThreshold = 20
	$maxEvents = 10000

	$logsWithPatterns = @(
		@("Security", 4625, @("Source Network Address")),
		@("OpenSSH/Admin", 2, @("maximum authentication attempts")),
		@("OpenSSH/Admin", 1, @("Timeout before authentication"))
	)

	$newIpAddressesToBan = $logsWithPatterns | %{
		$log = $_[0]
		$id = $_[1]
		$patterns = $_[2]
		write-host $log, " ", $id, " ", $patterns
		
		Get-WinEvent $log -maxevents $maxEvents | ? {$_.id -eq $id } | ?{$_.TimeCreated -ge $timeFrame } | %{ $_.message} | select-string  $patterns | %{ [regex]::match($_,'(\d{1,4}[.]\d{1,4}[.]\d{1,4}[.]\d{1,4})') } | ?{ $_.Success} | %{$_.Groups[0].Value} | group-object | ?{$_.Count -ge $attackThreshold} | %{$_.Name}
	}

	$newIpAddressesToBan = @(($newIpAddressesToBan | %{ $_.Substring(0, $_.lastIndexOf(".")) + ".0/255.255.255.0"}) | sort | get-unique)

	$existingBannedIpAddresses = @((get-netfirewallrule -displayname "Rule for banning" | Get-NetFirewallAddressFilter).RemoteAddress | sort | get-unique)

	$allIpAddresses = (($newIpAddressesToBan + $existingBannedIpAddresses) | sort | get-unique)

	netsh advfirewall firewall set rule name="Rule for banning" new remoteip=([string]::Join(",", $allIpAddresses))
	
	sleep -s 60
}

I get at most 10000 events from the last 15 minutes, and I look for at least 20 attempts from a given IP. I extract IPv4 addresses with a regexp. I also replace them with a network subnet with mask of 24 bits.

Just run this script in the background with Administrator privileges and it will automatically add new IP addresses to the firewall. You need to create the rule Rule for banning manually beforehand as you need. If you need some other patterns or event logs, just add them to the collection at the beginning.

]]>
https://blog.adamfurmanek.pl/2023/01/13/availability-anywhere-part-19/feed/ 0
Availability Anywhere Part 18 — Binding same port for multiple docker containers https://blog.adamfurmanek.pl/2023/01/06/availability-anywhere-part-18/ https://blog.adamfurmanek.pl/2023/01/06/availability-anywhere-part-18/#comments Fri, 06 Jan 2023 09:00:44 +0000 https://blog.adamfurmanek.pl/?p=4811 Continue reading Availability Anywhere Part 18 — Binding same port for multiple docker containers]]>

This is the eighteenth part of the Availability Anywhere series. For your convenience you can find other parts in the table of contents in Part 1 – Connecting to SSH tunnel automatically in Windows

Let’s say that you run a project with Docker that uses a webserver (like a frontend application). This application will need to talk to some other service, like backend or database. We already know hw to forward port from host to the container. This way we can host whatever service locally, and talk to it from the container. We can also expose the webserver port (be it 8080) to the host, so we can connect to it from our browser running locally.

Let’s now consider similar problem but in the opposite direction. Let’s say that we have multiple webservers running in multiple dockers, and we want to be able to connect to all of them simultaneously from the host. How to do that?

The problem we typically face is how we bind ports. When using docker, we typically do the following:

docker -p 8080:80

with docker-compose we would do this:

ports:
  - "8080:80"

However, we can’t expose multiple applications this way because only one binding is allowed. Technically, lines above try to bind port 80 in the container to the port 8080 on the default IP address of the Docker daemon (which is 0.0.0.0) or 0.0.0.0 for docker-compose. This effectively binds all available interfaces we have. So, how to deal with this? Let’s see multiple steps we can take.

First step – changing port bindings

That’s the easiest solution. Just change port bindings (to something like 8081:80) and off you go. The problem is that you need to keep a mental map of the bindings, you may need to change the connection strings, and you need to remember not to push these changes to the repository. The last one is especially tricky because you need to change the files for docker-compose that normally you want push to the repository.

Second step – using environment variables

12-Factor App Methodology tells us to use environment variables as much as possible. Based on that, we should change docker commands and docker-compose files to accept environment variables for port bindings. Since we can put these values in .env file and not push them to the repository, we can effectively change our configuration easily without changing the source code. However, since the connection strings are often in files that we need to push to the repository, this may still be not enough.

Unfortunately, this won’t work well if we decide to run the frontend locally (instead of running it in the docker). Since the frontend connects to the default port, this port won’t be available on the host (or will be mapped to some other application).

Third step – use separate network interfaces

Separate network interfaces can give us even more. When we bind ports like 8080:80, we actually bind them as in 0.0.0.0:8080:80. We already mentioned that 0.0.0.0 means “bind all interfaces”. However, we can change this to some other IP address. The question is which one to use.

Windows lets us create new loopback network interface with hdwwiz.exe. See the last section of this post for the instructions how to do that. Linux and Mac let us do similar with aliases and new loopback interfaces. What’s more, we can assign some new IP address to the interface (like 192.168.123.5) and use hosts file to map subdomain like MYPROJECT.localhost to that address.

This way we can easily separate the traffic. We now bind all ports to 192.168.123.5:80:80, change connection strings to sql://MYPROJECT.localhost:PORT, and finally connect to the application from the browser easily on MYPROJECT.localhost:80. We can also configure Multi-Account Containers in Firefox or FoxyProxy in Firefox/Chrome to connect via Socks proxy forwarded from the docker container, so we would just connect as we were in the docker.

The cool part is that this will work the same way regardless of running that in the docker or locally. If we run inside the docker, then MYPROJECT.localhost:PORT will resolve to something like 127.0.0.1:PORT which works well because we forward port from the host to the docker container. However, if we run it locally, then it will resolve to 192.168.123.5:PORT that is accessible from the host thanks to the interface.

What’s more, we can modify docker-compose and docker commands transparently and push changes to the repository. Other developers on the team won’t be affected, and we will be able to separate traffic from multiple projects locally. If some other developer doesn’t have new interface configured and hosts file modified, then MYPROJECT.localhost will resolve to 127.0.0.1 and will still work.

Creating new network interface with IP address

In Windows

Run hdwwiz.exe
Install the hardware that I manually select from the list (Advanced)
Network adapters
Microsoft -> Microsoft KM-TEST Loopback Adapter
Go to network adapters. Rename the new adapter to MYPROJECT. Manually set the IP address to something like 192.168.123.5. Set mask to 255.255.255.255. Set DNS IP to 8.8.8.8
Edit C:\windows\system32\drivers\etc\hosts and add the following line: 192.168.123.5 MYPROJECT.localhost

In Linux

ifconfig lo:1 192.168.123.5/24

In mac

ifconfig lo0 alias 192.168.123.5

]]>
https://blog.adamfurmanek.pl/2023/01/06/availability-anywhere-part-18/feed/ 1
Availability Anywhere Part 17 — Splitting physical monitor into multiple https://blog.adamfurmanek.pl/2022/12/23/availability-anywhere-part-17/ https://blog.adamfurmanek.pl/2022/12/23/availability-anywhere-part-17/#comments Fri, 23 Dec 2022 09:00:14 +0000 https://blog.adamfurmanek.pl/?p=4792 Continue reading Availability Anywhere Part 17 — Splitting physical monitor into multiple]]>

This is the seventeenth part of the Availability Anywhere series. For your convenience you can find other parts in the table of contents in Part 1 – Connecting to SSH tunnel automatically in Windows

Let’s say that we have a big physical screen that we would like to split into multiple screens. We would like to support a proper full-screen behavior, so not just like splitting a screen into windows, but screens that are completely independent from each other.

We can emulate this with OBS and Indirect Display Driver Sample. Install the driver as specified in GitHub to get additional 5 virtual monitors (not desktops).

Now, the trick is to juggle with OBS to manage screens. Currently, I have 3 physical screens numbered 1, 2, and 3. They are physically configured like this:

------- ------- -------
|     | |     | |     |
|  1  | |  2  | |  3  |
|     | |     | |     |
------- ------- -------

I have 5 additional virtual screens numbered 4, 5, 6, 7, 8. I turn off screen number 8, and other screens put to the left of my middle screen (2), like this:

------- ---- ---  ------- -------
|     | | 4 | 5 | |     | |     |
|  1  | ---- ---  |  2  | |  3  |
|     | | 6 | 7 | |     | |     |
------- ---------- ------- ------

Now, I start OBS, create a scene of the size of screen 1. I create 4 display captures for monitors 4, 5, 6, 7. I put them in the corners.
Finally, I select “Full Screen Projector Preview” and put it on the screen 1. This way I can move my mouse from screen 2 to the left to enter screens 5+7 and then 4+6. If I move the mouse pointer more to the left, then I end up in screen 1, but I don’t want to do it.

This works as physical monitors where I can use only screens 2-7. I can’t use screen 1 (as it would obscure screens 4-7), but that’s not a problem. I have 6 screens effectively at this point. Full screen works as expected. The problem is with connecting over RDP with multiple screens support, because then screen 1 obscures all screens 4-7. We could fix that by running similar OBS in the remote environment.

You can achieve the same in Mac with BetterDisplay.

]]>
https://blog.adamfurmanek.pl/2022/12/23/availability-anywhere-part-17/feed/ 1
Availability Anywhere Part 16 — Forwarding port from host to docker https://blog.adamfurmanek.pl/2022/12/17/availability-anywhere-part-16/ https://blog.adamfurmanek.pl/2022/12/17/availability-anywhere-part-16/#respond Sat, 17 Dec 2022 09:00:23 +0000 https://blog.adamfurmanek.pl/?p=4783 Continue reading Availability Anywhere Part 16 — Forwarding port from host to docker]]>

This is the sixteenth part of the Availability Anywhere series. For your convenience you can find other parts in the table of contents in Part 1 – Connecting to SSH tunnel automatically in Windows

Let’s say that we want to expose a port from the host machine to the docker container. For instance, we have a locally installed database and we want to access it from the docker container. If we’re on Linux, then we can use --network host and that will do the trick for us. However, this option is not supported on Mac or Windows. Let’s see how to do it in that case.

We are going to run an OpenSSH server inside the docker network, connect to it from the host, forward a remote port, and then connect to localhost.

Let’s start with creating a directory for the docker project:

mkdir -p ssh_tunnel

Let’s create SSH keys.

ssh-keygen -b 2048 -t rsa -f ssh_tunnel/tunnel_rsa

Next, create the configuration for OpenSSH. Just a default configuration with port forwarding enabled (AllowTcpForwarding set to yes). This is in file ssh_tunnel/sshd_config:

#	$OpenBSD: sshd_config,v 1.104 2021/07/02 05:11:21 dtucker Exp $

# This is the sshd server system-wide configuration file.  See
# sshd_config(5) for more information.

# This sshd was compiled with PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# The strategy used for options in the default sshd_config shipped with
# OpenSSH is to specify options with their default value where
# possible, but leave them commented.  Uncommented options override the
# default value.

Port 2222
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::

#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
#HostKey /etc/ssh/ssh_host_ed25519_key

# Ciphers and keying
#RekeyLimit default none

# Logging
#SyslogFacility AUTH
#LogLevel INFO

# Authentication:

#LoginGraceTime 2m
#PermitRootLogin prohibit-password
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10

#PubkeyAuthentication yes

# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
# but this is overridden so installations will only check .ssh/authorized_keys
AuthorizedKeysFile	.ssh/authorized_keys

#AuthorizedPrincipalsFile none

#AuthorizedKeysCommand none
#AuthorizedKeysCommandUser nobody

# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
#HostbasedAuthentication no
# Change to yes if you don't trust ~/.ssh/known_hosts for
# HostbasedAuthentication
#IgnoreUserKnownHosts no
# Don't read the user's ~/.rhosts and ~/.shosts files
#IgnoreRhosts yes

# To disable tunneled clear text passwords, change to no here!
PasswordAuthentication no
#PermitEmptyPasswords no

# Change to no to disable s/key passwords
#KbdInteractiveAuthentication yes

# Kerberos options
#KerberosAuthentication no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes
#KerberosGetAFSToken no

# GSSAPI options
#GSSAPIAuthentication no
#GSSAPICleanupCredentials yes

# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the KbdInteractiveAuthentication and
# PasswordAuthentication.  Depending on your PAM configuration,
# PAM authentication via KbdInteractiveAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and KbdInteractiveAuthentication to 'no'.
#UsePAM no

#AllowAgentForwarding yes
# Feel free to re-enable these if your use case requires them.
AllowTcpForwarding yes
GatewayPorts no
X11Forwarding no
#X11DisplayOffset 10
#X11UseLocalhost yes
#PermitTTY yes
#PrintMotd yes
#PrintLastLog yes
#TCPKeepAlive yes
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
#ClientAliveCountMax 3
#UseDNS no
PidFile /config/sshd.pid
#MaxStartups 10:30:100
#PermitTunnel no
#ChrootDirectory none
#VersionAddendum none

# no default banner path
#Banner none

# override default of no subsystems
Subsystem	sftp	internal-sftp

# Example of overriding settings on a per-user basis
#Match User anoncvs
#	X11Forwarding no
#	AllowTcpForwarding no
#	PermitTTY no
#	ForceCommand cvs server

And the ssh_tunnel/Dockerfile:

FROM lscr.io/linuxserver/openssh-server:latest

COPY sshd_config /config/ssh_host_keys/sshd_config

We could mount the volume instead, but I couldn’t get it to work with file permissions in Mac. Copying the file is good enough.

That’s it. We can now start the machinery. First, build the image:

docker build -t image_ssh ssh_tunnel

Now, let’s start or run the container with OpenSSH server:

docker start container_ssh 2>/dev/null || docker run -d \
  --name=container_ssh \
  -e TZ=Etc/UTC \
  -e "PUBLIC_KEY=YOUR_GENERATED_PUBLIC_KEY_HERE" \
  -p 127.0.0.1:58222:2222 \
  -p 127.0.0.1:YOUR_APPLICATION_PORT:YOUR_APPLICATION_PORT \
  -e USER_NAME=tunnel \
  --restart unless-stopped \
  image_ssh

Now, let’s clear the saved SSH fingerprint (in case when we restarted everything), kill existing session (if there is one), and connect to the container:

ssh-keygen -R '[localhost]:58222'
pkill -f "ssh -i $(pwd)/ssh_tunnel/tunnel_rsa tunnel@localhost -p 58222"
ssh -i "$(pwd)"/ssh_tunnel/tunnel_rsa tunnel@localhost -p 58222 -4 -o StrictHostKeyChecking=no -R YOUR_DEPENDENCY_PORT:127.0.0.1:YOUR_DEPENDENCY_PORT -fN

YOUR_DEPENDENCY_PORT is the port you’d like to forward to the container. This could be 5432 of your PostgreSQL server hosted locally, for instance. You can also see that we expose two ports from the container: 2222 as 58222 in order to connect to the OpenSSH server, and YOUR_APPLICATION_PORT which can be another application or dependency that you’d like to expose to another docker container or to external world.

Let’s now start or run our application:

docker start container_application 2>/dev/null || docker run \
  --name=container_application \
  --network 'container:container_ssh' \
  image_application

This way the application starts in the same network, so it can access localhost:YOUR_DEPENDENCYPORT that will go to the host via OpenSSH. At the same time, your container exposes YOUR_APPLICATION_PORT the regular way, so you can then configure additional tunnels.

You can clean all things up with the following (mind that it may remove some of your unused things):

ssh-keygen -R '[localhost]:58222'
pkill -f "ssh -i $(pwd)/ssh_tunnel/tunnel_rsa tunnel@localhost -p 58222"
docker stop container_application
docker rm container_application
docker rmi --force image_application
docker stop container_ssh 
docker rm container_ssh 
docker rmi image_ssh 
docker system prune
docker volume prune

Tested with Amazon Linux 2 and Ventura Mac with M1 chip. Should work with Windows as well, as long as you install OpenSSH client on your host (or use putty instead) and have some decent terminal. If you prefer to use PowerShell, then go with this code:

$location = (get-location).path
$Key = "${location}\ssh_tunnel\tunnel_rsa"
Icacls $Key /c /t /Inheritance:d
TakeOwn /F $Key
Icacls $Key /c /t /Grant:r ${env:UserName}:F
Icacls $Key /c /t /Remove:g Administrator "Authenticated Users" BUILTIN\Administrators BUILTIN Everyone System Users
Icacls $Key

docker build -t image_ssh ssh_tunnel

docker start container_ssh

if($LASTEXITCODE -ne 0){
docker run -d `
  --name=container_ssh`
  -e TZ=Etc/UTC `
  -e "PUBLIC_KEY=YOUR_GENERATED_PUBLIC_KEY_HERE" `
  -p 127.0.0.1:58222:2222 `
  -p 127.0.0.1:YOUR_APPLICATION_PORT:YOUR_APPLICATION_PORT `
  -e USER_NAME=tunnel `
  --restart unless-stopped `
  image_ssh
}

sleep 5

ssh-keygen -R '[localhost]:58222'
$replacedLocation = $location.replace("\", "/")
(Get-WmiObject win32_process -filter "Name='ssh.exe' AND CommandLine LIKE '%${replacedLocation}/ssh_tunnel/tunnel_rsa tunnel@localhost -p 58222%'").Terminate()
$sshStartBlock = { param([string]$pwd) ssh -i "$pwd/ssh_tunnel/tunnel_rsa" tunnel@localhost -p 58222 -4 -o StrictHostKeyChecking=no -R YOUR_DEPENDENCY_PORT:127.0.0.1:YOUR_DEPENDENCY_PORT -fN }
start-job -ScriptBlock $sshStartBlock -ArgumentList $replacedLocation

docker start container_application
if($LASTEXITCODE -ne 0){
docker run `
  --name=container_application `
  --network 'container:container_ssh' `
  image_application
}

This was tested with English-based Windows Server 2016 Datacenter. You may need to replace user names for different locales. Also, connecting to localhost should work with IPv4. If it defaults to IPv6, then change it to 127.0.0.1

Cleaning in PowerShell:

ssh-keygen -R '[localhost]:58222'
$location = (get-location).path
$replacedLocation = $location.replace("\", "/")
(Get-WmiObject win32_process -filter "Name='ssh.exe' AND CommandLine LIKE '%${replacedLocation}/ssh_tunnel/tunnel_rsa tunnel@localhost -p 58222%'").Terminate()
docker stop container_application
docker rm container_application
docker rmi --force image_application
docker stop container_ssh 
docker rm container_ssh 
docker rmi image_ssh 
docker system prune
docker volume prune

What’s more, we can make this dance with keys even simpler by using sish. That’s basically an OpenSSH server that allows for empty password authentication and other stuff, just like serveo.net

]]>
https://blog.adamfurmanek.pl/2022/12/17/availability-anywhere-part-16/feed/ 0