HP LTE Modem und der Linuxschlaf

Problem: Linux, HP EliteBook 850 G5 mit HP LTE Modem. Nach 1-12x zuklappen und Sleep erwacht das Modem nicht mehr aus dem Schlaf und man muss alles mögliche und unmögliche restarten (am besten aber rebooten) um Dornröschen wachzuküssen.

Ursache: Würde mal annehmen HP?

Lösung/Workaround: Es gibt ein Tool mit dem man bestimmte USB Geräte zurücksetzen kann: usb_modeswitch, man braucht „nur“ Vendor- und Product-ID von dem Gerät welche man mit lsusb rausfinden kann:

Danach einfach das Gerät resetieren und es sollte zurück ins Leben kommen:

sudo usb_modeswitch -v 03f0 -p a31d --reset-usb

Proxmox Host Firewall macht dicht

Problem: Single Host Proxmox Installation. 3 Linux Bridges – vmbr0 für den Host selbst, vmbr1 für die CorpNet Simulation, vmbr2 für die Internetsimulation. Keine bessere Möglichkeit als Host Firewall gefunden um vmbr1 und vmbr2 voneinander abzuschotten (also das Internetsimulation nicht die VMs in der CorpNetsimulation sieht) und folgende Regel eingetragen (123 ist Internet, 122 ist CorpNet):

Hat super funktioniert, auch über Reboots hinweg bis ich angefangen habe pve8to9 für Upgrade auf PVE 9.0 auszuführen und paar Warnings weggemacht habe (nix was eigentlich Firewall ändern sollte) – auf einmal ging nix mehr rein aber alles raus (VM und Host selber kamen überall hin).

Ursache: Unbekannt, irgendwas hat die Regel die vorher einwandfrei auch ohne Zusatzregeln funktioniert hat geändert.

Lösung (vermutlich grauslicher Hack aber für mich reichts): Da man ja remote nicht mehr drauf kommt lokal oder über KVM oder IPMI in /etc/pve/nodes/<hostname>/host.fw IN und OUT Regeln eintragen:

[OPTIONS]

nftables: 1

[RULES]

FORWARD DROP -source 192.168.123.0/24 -dest 192.168.122.0/24 -log nolog
OUT ACCEPT -log nolog
IN ACCEPT -log nolog

Und schon gehts wieder 😀

Global Secure Access: on-prem SSO mit PIN

Problem: Entra Joined Client mit Global Secure Access (Entra Private Access), Anmeldung des vom AD gesyncten Entra-Accounts erfolgt über PIN (=Windows Hello for Business). Zugriff auf on-prem Resourcen via SSO funktioniert nicht (es wird nach PIN gefragt um dann fehlenden Zugriff auf Domain Controller zu melden). Es gibt eine Enterprise Application mit den notwendingen Ports für den Domain Controller, Private DNS ist enabled – Zugriff wenn mit Passwort angemeldet funktioniert einwandfrei.

Ursache: Für SSO bei Anmeldung via WHfB muss was Zertifikat am Domain Controller angeht mehrere Punkte erfüllt sein:

  • Zertifikat am DC muss „KDC Authentication“ als EKU haben
  • Aktuelles Template dafür ist „Kerberos Authentication“, Domain Controller enrolled es aber nicht vollelektrisch
  • Client muss Root CA des DC Zertifikats in den Trusted Root CAs haben
  • CRL im CDP und AIA dürfen nicht (zuerst) auf LDAP liegen weil Client zum Zeitpunkt des Zugriffs zwar CRL prüfen muss aber noch kein Ticket hat um im AD den LDAP Zugriff machen zu dürfen

Lösung: Die Vorraussetzungen schaffen 😀

  • Auf der CA die CDP und AIA Extensions um LDAP erleichtern und HTTP enablen – je nach Geschmack auch gleich die Delta CRLs disablen.
  • Altes Zertifikat am DC löschen.
  • Alte Templates für Domain Controller von CA entfernen: Domain Controller, Domain Controller Authentication, Directory Email Replication.
  • Eine Group Policy erstellen oder bestehende die (auch) auf Domain Controller wirkt ändern sodass AutoEnrollment enabled ist (Template Kerberos Authentication hat schon AutoEnroll für DCs).
  • gpupdate/certutil -pulse/Reboot – was auch immer Autoenrollment heute triggert.
  • In der GSA Enterprise App für Domain Controller Access (oder in einer neuen App) ein Segment hinzufügen damit der Client durch den Tunnel auf den HTTP Ort wo die CRL liegt kommt – bei mir hats nur mit FQDN funktioniert.
  • Am Client das Root CA Zertifikat in die Trusted Root CAs importieren (Machine Store).

Quellen:

Global Secure Access: Breakglass mode

Problem: Global Secure Access Client (bei mir Entra Private Access) startet im Breakglass Modus, keine Verbindung.

Ursache: Im Eventlog zu GSA (Windows Komponente!) findet man beim Start Warnings der Art
ErrorMessage=AADSTS9002341: User is required to permit SSO, Microsoft hat offenbar aufgrund des EU DMA für bestimmte Aktionen andere Verhaltensweisen eingebaut wenn der Client in der EU steht – u.a. eben auch dass für jedes Mal SSO extra nachgefragt werden muss, schaut dann so aus:

Offenbar kann der Global Secure Access Client das nicht anzeigen und scheitert damit mit der Anmeldung und kann sich keine Policies holen.

Lösung/Workaround: Die Ausnahmebehandlung für die EU ist sehr lowtech – in C:\Windows\System32\IntegratedServicesRegionPolicySet.json sind Aktionen und die Ausnahmeregionen hinterlegt, das File ist natürlich mit TrustedInstaller geschützt also entweder händisch übernehmen und das Land der Wahl für die Policy mit der GUID 1d290cdb-499c-4d42-938a-9b8dceffe998 entfernen oder das Script im unten verlinkten Reddit-Beitrag verwenden (Achtung, löscht nur Region NL und versagt auf einem deutschen Windows weil die Administrators natürlich dort Administratoren heißen…) und rebooten.

Quellen:

Intune Portal auf Linux (Arch): Something happened [4u3ga]

Weil ich auf Schmerzen stehe hab ich versucht Intune Portal auf Arch Linux zum Laufen zu bringen, Anleitungen:

https://aur.archlinux.org/packages/intune-portal-bin

https://blog.strits.dk/how-to-enroll-arch-linux-in-microsoft-intune

https://wiki.archlinux.org/title/KDE_Wallet#Unlocking_KWallet_automatically_in_a_window_manager

(die Geschichte mit OpenSSL 3.3.2 unbedingt auch machen)

Ich bin wollte es aber superklug lösen und hab beim ersten Logon versucht mit FIDO2 Stick zu arbeiten was natürlich grandios in die Hose gegangen ist und danach bin ich ohne Angabe von Kennwort immer sofort hier gelandet:

Something went wrong. [4u3ga]

Auch tausend mal reinstallieren und alle bekannten Caches zu dem Produkt löschen hat nix gebracht.

Im Endeffekt wars aber der IdentityCache vom Edge den das Zeug offenbar tatsächlich benutzt (in den Fehlermeldungen von intune-portal stand auch immer was von oneauth…):

~/.cache/Microsoft/Edge/IdentityCache/OneAuth

Das gelöscht (und natürlich verliert dann der „normale“ Edge seine Identities) und schon gings….

OpenWrt 24.10.x NAT66 Performance

Weil ich mir jedes Mal wieder den Wolf suche hier mal verewigt: Mit Linux Kernel 6.6.x in OpenWrt 24.10 bricht die NAT66 Performance auf 10 MBit/s ein.

Mit einem beherzten Update auf 24.10.1 oder setup von ethtool in OpenWrt und einem ethtool -K wan rx-gro-list off kann man das auf 150 MBit/s heben.

Um auf einem MT7621 basierten Gerät (wie dem MikroTik RouterBoard 750Gr3) zur vollen Leistung zu kommen kann man in LuCI unter Network/Firewall das HW Offloading einschalten:

Entra Logoff bei Citrix StoreFront Logoff

Situation:

  • Edge im Kiosk Modus
  • StoreFront als Startseite
  • SAML Authentication gegen Entra (in weiterer Folge Logon am VDA via FAS)
  • Sehr kurzer Session Timeout wegen Kiosk
  • Kleinster Edge Kiosk Idle Zeitraum ist eine Minute
  • Citrix StoreFront Logoff macht KEIN Entra Logoff
    • man kann einfach auf „Anmelden“ klicken ist sofort wieder mit dem letzten SAML User drin weil die Cookies alle noch da sind

Lösung:

  • am StoreFront unter C:\Inetpub\wwwroot\Citrix\{Store}Web\Custom in script.js:
    CTXS.Extensions.afterWebLogoffComplete = function() {
    window.location.replace("https://login.microsoftonline.com/mytenant.onmicrosoft.com/oauth2/logout?post_logout_redirect_uri="+encodeURIComponent(window.location));
    callback();
    }

„Citrix Workspace-App ermitteln“ umgehen

Situation:

  • Edge im Kiosk Modus
  • StoreFront als Startseite
  • Desktop Auto Launch wird benötigt
    • benötigt protocolHandler=true in web.config
    • heißt CWA Detection muss laufen
  • Wir wollen aber direkt zur Anmeldung gelangen

Lösung:

  • am Kiosk Rechner
    • HKLM/SOFTWARE/Microsoft/Windows/AssignedAccessConfiguration/Profiles/{GUID}/AllowedApps/App0 beim Wert „Arguments“ hinten dran „--disable-features=Translate
  • beim Kiosk User (HKEY_USERS/{SID vom User}/SOFTWARE/Policies/Microsoft/Edge):
    • AutoOpenAllowedForURLs: 1 = „https://url.vom.storefront“
    • AutoOpenFileTypes: 1 = „ica“
    • AutoLaunchProtocolFromOrigins = „[{„allowed_origins“: [„https://url.vom.storefront“], „protocol“: „receiver“}]
  • am StoreFront unter C:\Inetpub\wwwroot\Citrix\{Store}Web\Custom in script.js:
    CTXS.Extensions.preInitialize = function() {
    CTXS.setCookie("CtxsClientDetectionDone","true");
    CTXS.setCookie("CtxsClientVersion","24.9.10.28"); //whatever
    CTXS.setCookie("CtxsHasUpgradeBeenShown","true");
    CTXS.setCookie("CtxsIsPassThrough","false");
    callback();
    }

Streamlit App+IIS als Proxy+Windows Authentication+angemeldeten Benutzer erfahren

Für das Hosting einer Streamlit App über IIS als Proxy gibts eine fantastische Anleitung: https://gist.github.com/stanorama/bb52930e3cf70138d7f199338c064ae6, kurz zusammengefasst falls der Gist mal verschwinden sollte:

  • In der IIS Rolle WebSocket Protocol enablen
  • In der IIS Rolle Windows Authentication enablen
  • Application Request Routing installieren (https://www.iis.net/downloads/microsoft/application-request-routing) und in der IIS Console bei den Actions des Features in „Server Proxy Settings“ Proxy einschalten
  • URL Rewrite installieren (https://www.iis.net/downloads/microsoft/url-rewrite)
  • Die User Site erzeugen, gerne auch mit https
  • Windows Authentication auf die Site enablen und Provider auf nur NTLM umstellen (jaaaa, grausam), Site Directory ACL setzen oder in den Authentication Rules entsprechende Gruppen/User hinterlegen
  • URL Rewrite Regel (inbound) erzeugen mit Match URL = .* Rewrite auf http://localhost:1234/{R:0} (dort wo halt die Streamlist App läuft) erzeugen

Resultierende web.config:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="Reverse proxy to streamlit application running on localhost">
                    <match url=".*" />
                    <action type="Rewrite" url="http://localhost:8511/{R:0}" />
                    <conditions>
                        <add input="{HTTP_HOST}" pattern=".*" />
                    </conditions>
                    <serverVariables>
                    </serverVariables>
                </rule>
            </rules>
        </rewrite>
        <security>
            <authentication>
                <windowsAuthentication enabled="true">
                    <providers>
                        <clear />
                        <add value="NTLM" />
                    </providers>
                </windowsAuthentication>
            </authentication>
        </security>
    </system.webServer>
    <system.web>
        <authorization>
            <allow roles="The_group_with_access" />
        </authorization>
    </system.web>
</configuration>

Im Home vom Streamlit App startenden User .streamlit Verzeichnis erzeugen und darin config.toml (sonst funktioniert WebSocket Weiterleitung nicht):

[server]
enableCORS = false
enableXsrfProtection = false

Streamlit App starten:

<pfad mit venv wo streamlist installiert ist>\streamlit.exe run C:\some_streamlist_app\app.py --server.headless true --server.port 8511 --browser.serverAddress localhost

In Anlehnung an https://gist.github.com/Chibitko/e5366851cf47b6fb238a49d6e0d37b94 zum Decodieren des NTLM Authorization Headers kann man st.context.headers["Authorization"] dieser Funktion übergeben und bekommt User, Domäne und Clientname geliefert:

VALID_CHRS = set(string.ascii_letters + string.digits + string.punctuation)
def clean_str(st):
    return ''.join((s if s in VALID_CHRS else '?') for s in st)

class StrStruct(object):
    def __init__(self, pos_tup, raw):
        length, alloc, offset = pos_tup
        self.length = length
        self.alloc = alloc
        self.offset = offset
        self.raw = raw[offset:offset+length]
        self.utf16 = False

        if len(self.raw) >= 2 and self.raw[1] == 0:
            self.string = self.raw.decode('utf-16')
            self.utf16 = True
        else:
            self.string = self.raw
        
    def __str__(self):
        return clean_str(self.string)


def decode_ntlmssp(header_raw):
    try:
        header = base64.b64decode(header_raw[5:])
    except:
        return "","",""

    if header[:8] != b"NTLMSSP\0":
        return "","",""

    ver_tup = struct.unpack("<i", header[8:12])
    ver = ver_tup[0]
    if ver != 3:
       return "","",""
    hdr_tup = struct.unpack("<hhihhihhihhihhi", header[12:52])

    domain=StrStruct(hdr_tup[6:9], header).string
    user=StrStruct(hdr_tup[9:12], header).string
    client=StrStruct(hdr_tup[12:15], header).string

    return client.upper(),domain.upper(),user.upper()

Custom Etikettengröße bei Drucker setzen

Als Erweiterung zu https://roman.gallauner.at/default-papiergroesse-papersize-eines-druckers-mit-powershell-aendern/ hier eine Funktion mit der man benutzerdefinierte Etikettengrößen pro Drucker setzen kann (wenn PaperSize im vordefinierten Layout mal „fluktuiert“ – soll ja angeblich passieren…):

param([switch]$Debug)

# https://www.codeproject.com/Articles/6899/Changing-printer-settings-using-C

$c=@'
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
using Microsoft.Win32;

   public class PrinterSettings
   {
      [DllImport("kernel32.dll", EntryPoint = "GetLastError", SetLastError = false, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
      private static extern Int32 GetLastError();

      [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
      private static extern bool ClosePrinter(IntPtr hPrinter);

      [DllImport("winspool.Drv", EntryPoint="DocumentPropertiesA", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
      private static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, [MarshalAs(UnmanagedType.LPStr)] string pDeviceName, IntPtr pDevModeOutput, ref IntPtr pDevModeInput, int fMode);

      [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
      private static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr PrinterDefaults);

      [DllImport("winspool.drv", CharSet = CharSet.Ansi, SetLastError = true)]
      private static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr pPrinter, int Command);

      [StructLayout(LayoutKind.Sequential)]
      public struct PRINTER_INFO_9
      {
         public IntPtr pDevMode;
      }

      private const short CCDEVICENAME = 32;
      private const short CCFORMNAME = 32;
      [StructLayout(LayoutKind.Sequential)]
      public struct DEVMODE
      {
         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCDEVICENAME)]
         public string dmDeviceName;
         public short dmSpecVersion;
         public short dmDriverVersion;
         public short dmSize;
         public short dmDriverExtra;
         public int dmFields;
         public short dmOrientation;
         public short dmPaperSize;
         public short dmPaperLength;
         public short dmPaperWidth;
         public short dmScale;
         public short dmCopies;
         public short dmDefaultSource;
         public short dmPrintQuality;
         public short dmColor;
         public short dmDuplex;
         public short dmYResolution;
         public short dmTTOption;
         public short dmCollate;
         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCFORMNAME)]
         public string dmFormName;
         public short dmUnusedPadding;
         public short dmBitsPerPel;
         public int dmPelsWidth;
         public int dmPelsHeight;
         public int dmDisplayFlags;
         public int dmDisplayFrequency;
      }
      private const int PRINTER_ACCESS_USE = 0x8;
      private const int DM_OUT_BUFFER = 0x2;
      private const int DM_MODIFY = 0x8;
      private const int DM_PAPERSIZE = 0x2;
      private const int DM_PAPERLENGTH = 0x4;
      private const int DM_PAPERWIDTH = 0x8;

      public static bool ChangePaperSize(string sPrinterName, short nSize, short nWidth, short nHeight, out string sDebug)
      {
         DEVMODE oDM;
         IntPtr pNativeDM = IntPtr.Zero;
         int nBytesNeeded = 0;
         IntPtr hPrinter = new System.IntPtr();
         PRINTER_INFO_9 oPrinterInfo;
         IntPtr pNativePrinterInfo = IntPtr.Zero;
         IntPtr pDummy=IntPtr.Zero;
         bool bChanged=false;

         sDebug="";

         //Console.WriteLine("Open printer");
         if (!OpenPrinter(sPrinterName, out hPrinter, IntPtr.Zero))
         {
            Console.WriteLine("OpenPrinter() failed with error "+Marshal.GetLastWin32Error()+", aborting.");
            sDebug += "OpenPrinter() failed with error "+Marshal.GetLastWin32Error()+", aborting.\n";
            return false;
         }

         //Console.WriteLine("Getting bytes needed for DEVMODE via DocumentProperties");
         nBytesNeeded=DocumentProperties(IntPtr.Zero,hPrinter,sPrinterName,IntPtr.Zero,ref pDummy,0);
         //Console.WriteLine("Bytes needed: "+nBytesNeeded+", allocating.");
         pNativeDM=Marshal.AllocHGlobal(nBytesNeeded);
         //Console.WriteLine("Fetching DEVMODE via DocumentProperties");
         DocumentProperties(IntPtr.Zero,hPrinter,sPrinterName,pNativeDM,ref pDummy,DM_OUT_BUFFER);
         oDM = (DEVMODE)Marshal.PtrToStructure(pNativeDM, typeof(DEVMODE));
         //Console.WriteLine("Free native DM");
         Marshal.FreeHGlobal(pNativeDM);

         //Console.WriteLine("Old size: "+oDM.dmPaperSize);
         if (oDM.dmPaperSize != nSize)
         {
            Console.WriteLine("dmPaperSize="+oDM.dmPaperSize+" => "+nSize);
            sDebug += "dmPaperSize="+oDM.dmPaperSize+" => "+nSize + "\n";
            oDM.dmPaperSize = nSize;
            oDM.dmFields |= DM_PAPERSIZE;
            bChanged=true;
         }

         if (nWidth != 0 && oDM.dmPaperWidth != nWidth) 
         { 
            Console.WriteLine("dmPaperWidth="+oDM.dmPaperWidth+" => " +nWidth);
            sDebug += "dmPaperWidth="+oDM.dmPaperWidth+" => " +nWidth + "\n";
            oDM.dmPaperWidth=nWidth; 
            oDM.dmFields |= DM_PAPERWIDTH; 
            bChanged=true;
         }
         if (nHeight != 0 && oDM.dmPaperLength != nHeight) 
         { 
            Console.WriteLine("dmPaperLength="+oDM.dmPaperLength+" => " +nHeight);
            sDebug += "dmPaperLength="+oDM.dmPaperLength+" => " +nHeight + "\n";
            oDM.dmPaperLength=nHeight; 
            oDM.dmFields |= DM_PAPERLENGTH; 
            bChanged=true;
         }

         if (bChanged)
         {
            //Console.WriteLine("Allocating memory for new native DEVMODE");
            pNativeDM = Marshal.AllocHGlobal(Marshal.SizeOf(oDM));
            //Console.WriteLine("Copy managed DEVMODE to native memory");
            Marshal.StructureToPtr(oDM, pNativeDM, true);

            //Console.WriteLine("Modifying DEVMODE with DocumentProperties");
            DocumentProperties(IntPtr.Zero,hPrinter,sPrinterName,pNativeDM,ref pDummy,DM_MODIFY);

            oPrinterInfo.pDevMode = pNativeDM;
            //Console.WriteLine("Copy managed PRINTER_INFO_9 to native memory");
            pNativePrinterInfo = Marshal.AllocHGlobal(Marshal.SizeOf(oPrinterInfo));
            Marshal.StructureToPtr(oPrinterInfo, pNativePrinterInfo, true);
            //Console.WriteLine("Writing new PRINTER_INFO_9 to printer");
            if (!SetPrinter(hPrinter, 9, pNativePrinterInfo, 0))
            {
               Console.WriteLine("SetPrinter() failed with error "+Marshal.GetLastWin32Error()+", aborting.");
               sDebug += "SetPrinter() failed with error "+Marshal.GetLastWin32Error()+", aborting.\n";
               Cleanup(pNativeDM, pNativePrinterInfo, hPrinter);
               return false;
            }
            Cleanup(pNativeDM, pNativePrinterInfo, hPrinter);
         }
         else
         {
            Console.WriteLine("Nothing to do, exiting.");
            sDebug += "Nothing to do, exiting.\n";
         }
         return true;
      }

      public static void Cleanup(IntPtr pNativeDM,IntPtr pNativePrinterInfo,IntPtr hPrinter)
      {
         try
         {
            //Console.WriteLine("Memory and handle cleanup");
            if (hPrinter != IntPtr.Zero) ClosePrinter(hPrinter);
            if (pNativeDM != IntPtr.Zero) Marshal.FreeHGlobal(pNativeDM);
            if (pNativePrinterInfo != IntPtr.Zero) Marshal.FreeHGlobal(pNativePrinterInfo);
         }
         catch (Exception)
         {
         }
      }
   }
'@

if ($Debug)
{
   Start-Transcript "$($env:Temp)\PaperSize.log"
}

Add-Type -TypeDefinition $c -ReferencedAssemblies System.Runtime.InteropServices,System.ComponentModel 
Add-Type -AssemblyName System.Drawing

$printers="\\printserver\printer1~495~360#\\printserver\printer2~USER~495~360"

foreach ($printerinfo in $printers.Split('#'))
{
   Write-Host "====================================================="
   $printer=$printerinfo.Split('~')[0]
   $paper=$printerinfo.Split('~')[1]
   $width=$printerinfo.Split('~')[2]
   $height=$printerinfo.Split('~')[3]

   $ps=New-Object System.Drawing.Printing.PrinterSettings
   $ps.PrinterName=$printer
   $size=$ps.PaperSizes|? PaperName -eq $paper

   if ($null -ne $size) 
   {
       Write-Host "$($printer): size (raw): $($size.RawKind)"

       $messages=""
       $success=[PrinterSettings]::ChangePaperSize($printer,$size.RawKind,$width,$height,[ref]$messages)
       if ($Debug) { $messages }
       if ($success)
       {
          Write-Host "   ==> SUCCESS"
       }
       else
       {
          Write-Host "   ==> ERROR"
       }
   }
   else
   {
      Write-Host "$($printer): PaperSize $($paper) not found."
   }
}

if ($Debug)
{
   Stop-Transcript
}