Wirtualizacja wine 32bit w systemie 64bit przy wykorzystaniu kontenera LXC

Kategoria: Artykuły, etykiety: audio, lxc, ssh, szyfrowanie, video, wine, wirtualizacja

Dodany: 2014-03-28 14:16 (zmodyfikowany: 2014-04-17 05:27)
Przez: morfik

Wyświetleń: 17084

Każdy kto zmienił system z i386 na amd64 raczej nie dostrzegł większej różnicy w operowaniu na którymś z nich, czasem jedynie pakiety w nazwie mają 64 zamiast 32. Jednak jest jedna rzecz, która drażni chyba każdego. Mowa tutaj o wine, bo jego rozwój został daleko za murzynami, już pominę, że nie umie obsługiwać natywnie pulse (w ogóle nie umie...) to jeszcze dochodzi do tego pierdolnik jaki nam robi w systemie wine 32bit, którego zwykle ludzie używają do odpalania gier, czy programów.

Jeśli chcielibyśmy zainstalować wine na maszynie 64bit w debianie, trzeba by dodać architekturę 32bit i doinstalować xxx pakietów (w tym też sterowniki do grafy), wszystko 32bitowe by w ogóle mieć możliwość zainstalowania wine. Ja może nie gram za dużo i praktycznie nie korzystam z wine ale gdy zmieniłem architekturę systemu, miałem w planach opracować sposób odseparowania wine, nie tylko tego 32 bitowego, jeśli już jest 64 bitowy, od reszty systemu.

W końcu udało mi się to zrobić. Nie wiem jak tam wyglądają prace na wine64 i czy da radę na nim odpalać aplikacje albo gry 32 bitowe, w każdym razie tutaj zostanie rozważony wariant grania w starsze gry na wine32. Raczej nie będzie większego problemu z przeniesieniem całej konfiguracji na wine64.

By odseparować jedną część systemu od drugiej, potrzeba czegoś na wzór maszyny wirtualnej, np. virtalbox. Problem z vboxem jest taki, że po pierwsze trzeba mu przydzielić określone zasoby oraz wymaga od procesora wsparcia wirtualizacji, a jak wiadomo, nie wszystkie procki mają jeszcze ten ficzer, przynajmniej mój nie ma. Zamiast więc posługiwać się rozwiązaniem w stylu virtualboxa, skorzystamy z LXC.

1. Konfiguracja systemu pod LXC

LXC to pseudo wirtualny system, który działa jak zwykły chroot ale posiada kilka znaczących różnic, choćby urządzenia -- w chroot nie ma możliwości określenia z jakich urządzeń chroot będzie korzystał -- bierze cały /dev/ , a LXC może określić, którym urządzeniom zezwolić na działanie. LXC nie korzysta z ustawień sieci hosta -- można na nim ustawić NAT i w iptables przepuszczać/blokować połączenia. Jest też spora różnica w procesach, bo w chroot jak zamontujemy katalog /proc , to ujrzymy wszystkie procesy dostępne w całym systemie, a LXC pozwala zobaczyć jedynie tylko te procesy, które działają w jego obrębie. Naturalnie, host dalej ma możliwość wglądu w procesy wewnątrz kontenera LXC i może dowolnie nimi zarządzać. Dodatkowo LXC jest kontrolowany przez cgroups i można w ten sposób przydzielać zasoby pod określone kontenery. Można nawet zawiesić (zamrozić) taki kontener, by nie pobierał żadnych zasobów, aż do czasu odmrożenia.

I tu zaczynają się schody, bo o ile mechanizm cgroups jest, lub może się okazać, użyteczny, to LXC nie może bez niego żyć i wszystko by było w porządku gdyby nie fakt, że obecnie pakiety od cgroups i LXC się żrą w debianie. Idzie je co prawda pogodzić ale najniższa wersja LXC, którą da radę zainstalować, przy jednoczesnym posiadaniu w systemie cgroups, pochodzi z gałęzi experimental, więc to nawet nie jest sid i raczej wątpię, by ktoś na stable ten mechanizm wdrożył.

Jeśli jednak nie boimy się wyzwań, możemy dodać eksperymentalną gałąź do /etc/apt/sources.list :

 deb     http://ftp.pl.debian.org/debian/ experimental main contrib non-free
#   deb-src http://ftp.pl.debian.org/debian/ experimental main contrib non-free

Instalujemy także potrzebne pakiety:

# aptitude -t experimental install lxc

W chwili pisania tego tekstu, pakiet lxc ma wersję 1.0.0-4 .

Dodatkowo trzeba zainstalować cgroups:

# aptitude install cgroup-bin libcgroup1

Cgroups także są pobugowane w debianie, zawierają skrypty z redhata, które nie działają na debianie i pewnie jest cała masa innych bugów, których jeszcze nie doświadczyłem -- jednym słowem zajebiście. Być może samo zamontowanie cgroups przez fstab otworzy drogę do używania kontenerów LXC. Poniżej jest linijka z wiki debiana, którą trzeba dodać do /etc/fstab :

cgroup  /sys/fs/cgroup  cgroup  defaults  0   0

U mnie jednak było parę błędów przy startowaniu kontenera. Nie wiem czy, któryś z nich był krytyczny, generalnie to w miarę działało. Ja już z cgroups miałem do czynienia wcześniej i napisałem inny artykuł na temat konfiguracji cgroups i ten setup przedstawiony w tamtym artykule działa bez zarzutu. Także warto też rzucić okiem na niego i spróbować skonfigurować montowanie cgroups ręcznie.

W tej chwili powinniśmy mieć już skonfigurowane cgroups, a lxc-checkconfig powinien mieć zapalone wszędzie zielone lampki, tak jak w logu poniżej:

# lxc-checkconfig
Kernel configuration not found at /proc/config.gz; searching...
Kernel configuration found at /boot/config-3.13-1-amd64
--- Namespaces ---
Namespaces: enabled
Utsname namespace: enabled
Ipc namespace: enabled
Pid namespace: enabled
User namespace: enabled
Network namespace: enabled
Multiple /dev/pts instances: enabled

--- Control groups ---
Cgroup: enabled
Cgroup clone_children flag: enabled
Cgroup device: enabled
Cgroup sched: enabled
Cgroup cpu account: enabled
Cgroup memory controller: enabled
Cgroup cpuset: enabled

--- Misc ---
Veth pair device: enabled
Macvlan: enabled
Vlan: enabled
File capabilities: enabled

Note : Before booting a new kernel, you can check its configuration
usage : CONFIG=/path/to/config /usr/bin/lxc-checkconfig

2. Przygotowanie kontenera LXC

Jeśli tak jest w istocie, przechodzimy do zbudowania kontenera:

# lxc-create -n wine32 -t debian -P /media/Server/lxc_wine/ -- -r sid -a i386
debootstrap is /usr/sbin/debootstrap
Checking cache download in /var/cache/lxc/debian/rootfs-sid-i386 ...
Downloading debian minimal ...
I: Retrieving Release
I: Retrieving Release.gpg
I: Checking Release signature
I: Valid Release signature (key id A1BD8E9D78F7FE5C3E65D8AF8B48AD6246925553)
I: Retrieving Packages
I: Validating Packages
I: Resolving dependencies of required packages...
I: Resolving dependencies of base packages...
...
I: Base system installed successfully.
Download complete.
Copying rootfs to /media/Server/lxc_wine/wine32/rootfs...Generating locales (this might take a while)...
  en_US.UTF-8... done
Generation complete.
Creating SSH2 RSA key; this may take some time ...
Creating SSH2 DSA key; this may take some time ...
Creating SSH2 ECDSA key; this may take some time ...
Creating SSH2 ED25519 key; this may take some time ...
invoke-rc.d: policy-rc.d denied execution of start.

Current default time zone: 'Europe/Warsaw'
Local time is now:      Fri Mar 28 10:51:16 CET 2014.
Universal Time is now:  Fri Mar 28 09:51:16 UTC 2014.

Root password is 'root', please change !

Parametry w powyższej linijce oznaczają:
-n -- nazwa kontenera
-t -- szablon użyty do zbudowania systemu
-P -- ścieżka do zapisu plików kontenera
-r -- release tego co zostało użyte w opcji -t
-a -- architektura budowanego systemu

Gdy budujemy kontener po raz pierwszy, zostanie zainicjowany debootstrap i pobierze, zainstaluje i skonfiguruje on minimalny system. W przypadku budowania kolejnego kontenera (ta sama architektura, ta sama gałąź), zostanie wykorzystany ten poprzedni minimalny system i nie będzie trzeba pobierać go z sieci jeszcze raz.

Edytujemy teraz plik konfiguracyjny kontenera, u mnie znajduje się on w /media/Server/lxc_wine/wine32/config . To w nim precyzujemy, urządzenia, punkty montowania oraz konfigurację sieci dla kontenera. Dobór urządzeń może sprawić problemy -- trzeba po prostu zajrzeć w katalog /dev/ na hoście w celu określenia parametrów urządzeń tam się znajdujących ale tylko tych, które będą nam potrzebne.

Jeśli zamierzamy korzystać z karty graficznej, a zamierzamy, kluczową rolę grają tutaj sterowniki. Ja korzystam z karty nvidii i mam do wyboru, albo zamknięte stery, albo te otwarte z modułem nouveau. To jakie sterowniki wybierzemy wpływa na urządzenia tworzone w katalogu /dev/ . Na zamkniętych sterach nvidii, mamy urządzenia /dev/nvidia* , na otwartych jest katalog /dev/dri/ i w nim kilka urządzeń. Potrzebne będą także urządzenia od myszy oraz klawiatury, no i od dźwięku.

Poniżej jest plik konfiguracyjny dla mojego kontenera LXC:

# Template used to create this container: /usr/share/lxc/templates/lxc-debian
# Parameters passed to the template: -r sid -a i386
# For additional config options, please look at lxc.conf(5)
lxc.rootfs = /media/Server/lxc_wine/wine32/rootfs

# Common configuration
lxc.include = /usr/share/lxc/config/debian.common.conf

# Container specific configuration
lxc.mount = /media/Server/lxc_wine/wine32/fstab
lxc.utsname = wine32
lxc.arch = i386

#lxc.network.type = empty
lxc.network.type = veth
lxc.network.name = veth0
lxc.network.flags = up
lxc.network.link = br0
lxc.network.veth.pair = veth0-sid
lxc.network.ipv4 = 192.168.1.2/24
lxc.network.ipv4.gateway = 192.168.1.253


# nvidia
lxc.cgroup.devices.allow = c 195:0 rwm
lxc.cgroup.devices.allow = c 195:255 rwm

# dri -- card0 controlD64
#lxc.cgroup.devices.allow = c 226:0 rwm
#lxc.cgroup.devices.allow = c 226:64 rwm

# input
lxc.cgroup.devices.allow = c 13:* rwm

# snd 
lxc.cgroup.devices.allow = c 116:* rwm

# mount
lxc.mount.entry=/media/Server/wow /media/Server/lxc_wine/wine32/rootfs/wow none bind 0 0

2.1. Konfiguracja sieci

Powyżej mamy ustawioną konfigurację sieci dla kontenera LXC, jednak nie zadziała ona OOTB, trzeba skonfigurować mostek (bridge), w przeciwnym razie nie będziemy mieli dostępu do internetu w kontenerze.

By móc stworzyć wirtualny interfejs dla mostka, potrzebne nam są narzędzia, a te są dostarczane przez pakiet bridge-utils . Instalujemy go i dopisujemy poniższe linijki do pliku /etc/network/interfaces :

auto br0
iface br0 inet static
      address 192.168.1.253
      netmask 255.255.255.0
      broadcast 192.168.1.255
      bridge_ports none
      
up iptables -t nat -F POSTROUTING
up iptables -t nat -A POSTROUTING -o eth0 -s 192.168.1.0/24 -d 0/0 -j MASQUERADE

Ustawiamy również odpowiednie reguły w iptables dla łańcucha FORWARD :

iptables -t filter -A FORWARD -s 192.168.1.0/255.255.255.0 -d 0/0 -j ACCEPT
iptables -t filter -A FORWARD -s 0/0 -d 192.168.1.0/255.255.255.0 -j ACCEPT

Włączamy także forwarding dla pakietów sieciowych w kernelu. Dopisujemy w /etc/sysctl.conf poniższe linijki:

net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1

Niektóre opcje definiowane w /etc/sysctl.conf mogą nie zostać załadowane na starcie systemu, czego efektem będzie brak sieci w kontenerze LXC po restarcie maszyny głównej. Możemy dopisać poniższą linijkę w /etc/rc.local :

sysctl -p >/dev/null 2>&1

Jeśli z jakichś powodów chcemy uczynić kontener czymś w rodzaju środowiska testowego, np. będziemy chcieli się łączyć po ssh, czy stawiać jakieś serwery, musimy przepuścić dodatkowy ruch w iptables. W zależności od tego jak się będziemy podłączać do kontenera, reguły nie będą takie same, bo raz będzie nawiązane połączenie z mostkiem, a innym razem z maszyną hosta -- wyskrobałem coś takiego dla iptables:

iptables -t filter -A INPUT -s 192.168.1.0/24 -d 192.168.1.0/24 -j ACCEPT
iptables -t filter -A INPUT -s 192.168.1.0/24 -d 10.1.4.41 -j ACCEPT
iptables -t filter -A OUTPUT -s 192.168.1.0/24 -d 192.168.1.0/24 -j ACCEPT
iptables -t filter -A OUTPUT -s 10.1.4.41 -d 192.168.1.0/24 -j ACCEPT

gdzie 10.1.4.41 to realne ip mojej maszyny, a 192.168.1.0/24 to sieć kontenera, w której znajduje się również ip mostka.

Dobrze jest też dodać poniższe wpisy dla ping, na wypadek gdyby były problemy z połączeniem:

iptables -t filter -A INPUT --protocol icmp --jump ICMP
iptables -t filter -A ICMP --protocol icmp --jump ACCEPT

Przy czym trzeba pamiętać, że system w kontenerze nie zawiera narzędzia ping -- trzeba doinstalować pakiet iputils-ping . Jak to zrobić nie mając dostępu do sieci? Trzeba po prostu zwyczajnie się chrootnąć do kontenera.

Restartujemy teraz połączenie sieciowe lub uruchamiamy komputer ponownie, by mieć pewność, że wszystko zostało skonfigurowane i załadowane jak należy. Jeśli jesteśmy pewni, że wszystko zrobiliśmy jak trza, możemy zwyczajnie wklepać poniższą linijkę:

# /etc/init.d/networking restart

Jeśli teraz podejrzymy interfejsy sieciowe (przez ifconfig albo ip addr show), powinniśmy ujrzeć br0 :

# ifconfig
...
br0       Link encap:Ethernet  HWaddr 00:00:00:00:00:00
          inet addr:192.168.1.253  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::bc0f:3bff:fee8:3dc4/64 Scope:Link
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:1330 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1656 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:137020 (133.8 KiB)  TX bytes:2374307 (2.2 MiB)

Przy włączonym kontenerze, powinien być widoczny także wirtualny interfejs kontenera:

...
veth0-sid Link encap:Ethernet  HWaddr fe:87:75:07:82:a2
          inet6 addr: fe80::fc87:75ff:fe07:82a2/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:40 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:648 (648.0 B)  TX bytes:4042 (3.9 KiB)

I powinniśmy mieć dodatkową trasę w tablicy routingu:

# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         10.1.255.253    0.0.0.0         UG    0      0        0 eth0
10.1.0.0        *               255.255.0.0     U     0      0        0 eth0
192.168.1.0     *               255.255.255.0   U     0      0        0 br0

Jeśli chodzi o samą konfigurację mostka, to wygląda ona tak:

# brctl show
bridge name     bridge id               STP enabled     interfaces
br0             8000.000000000000       no

Połączenie z kontenerem już powinniśmy uzyskać ale potrzebujemy jeszcze konfiguracji resolvera. Edytujemy zatem plik /media/Server/lxc_wine/wine32/rootfs/etc/resolv.conf i dodajemy tam poniższe wpisy:

nameserver 208.67.222.222
nameserver 208.67.220.220

Dobrze jest także ustawić plik /etc/hosts zarówno na hoście jak i w kontenerze, zwłaszcza jeśli chcemy używać jakichś usług, np. apache , niemniej zawsze można operować na cyferkach. Ja sobie dodałem coś takiego:

Plik /etc/hosts :

192.168.1.2 lxc.mhouse lxc

Plik /media/Server/lxc_wine/wine32/rootfs/etc/hosts :

10.1.4.41 morfikownia.mhouse morfikownia

Jeśli zamierzamy instalować sporo rzeczy możemy rozważyć edycję pliku sources.list w kontenerze (/media/Server/lxc_wine/wine32/rootfs/etc/apt/sources.list), zmieniając domyślny mirror na bardziej nam odpowiadający, np:

deb     http://ftp.pl.debian.org/debian/ sid main non-free contrib
#   deb-src http://ftp.pl.debian.org/debian/ sid main non-free contrib

3. Kontener LXC od środka

Możemy teraz podnieść kontener. Przy czym, taka uwaga -- w configu kontenera LXC umieściłem także jeden wpis od montowania lokalnego zasobu. Jeśli chcemy coś w taki sposób montować, musimy się upewnić, że katalog docelowy, czyli ten w kontenerze, istnieje, w przeciwnym razie dostaniemy poniższy błąd:

lxc-start: No such file or directory - failed to mount '/media/Server/wow' on '/usr/lib/x86_64-linux-gnu/lxc/rootfs//wow'
lxc-start: failed to setup the mount entries for 'wine32'
lxc-start: failed to setup the container
lxc-start: invalid sequence number 1. expected 2
lxc-start: failed to spawn 'wine32'

By wystartować kontener, wklepujemy do terminala taką oto linijkę:

# lxc-start -n wine32 -P /media/Server/lxc_wine/

Logujemy się za pomocą użytkownika root oraz hasła root i sprawdzamy czy działa sieć:

Debian GNU/Linux jessie/sid wine32 console

wine32 login: root
Password:

root@wine32:~# apt-get update
Get:1 http://ftp.pl.debian.org sid InRelease [205 kB]
Get:2 http://ftp.pl.debian.org sid/contrib Translation-en [42.3 kB]
Get:3 http://ftp.pl.debian.org sid/main Translation-en [4,537 kB]
Get:4 http://ftp.pl.debian.org sid/non-free Translation-en [77.1 kB]
Get:5 http://ftp.pl.debian.org sid/main i386 Packages [6,657 kB]
Get:6 http://ftp.pl.debian.org sid/non-free i386 Packages [88.5 kB]
Get:7 http://ftp.pl.debian.org sid/contrib i386 Packages [53.5 kB]
Fetched 11.7 MB in 15s (752 kB/s)
Reading package lists... Done

Zatrzymać kontener możemy albo przez zalogowanie się do niego i wydanie zwykłego polecenia zamykającego system, np. shutdown z odpowiednimi opcjami, czy też poweroff. Możemy także wyłączyć kontener z maszyny hosta przy pomocy:

# lxc-stop -n wine32 -P /media/Server/lxc_wine/

3.1. Problemy z udev i ręczne tworzenie urządzeń

Udev nie działa z kontenerami LXC i przy startowaniu kontenera zwraca takie oto ostrzeżenie:

udev does not support containers, not started ... (warning).

Nie ma się co tym przejmować, tylko nie mamy dostępu do pewnych urządzeń, tych charakterystycznych dla naszej maszyny. Skoro udev nie może ich stworzyć, sami musimy to zrobić. Urządzenia, które nas interesują znajdują się w:
/dev/input/* -- klawiatura oraz myszka
/dev/snd/* -- dźwięk
/dev/nvidia* lub /dev/dri/* -- w zależności od sterowników, grafika

Urządzenia tworzymy przy pomocy mknod . Zaglądamy zatem do katalogu /dev i dla przykładu, urządzenie nvidia0 wygląda tak:

$ ls -al /dev/nvidia0
crw-rw-rw-+ 1 root root 195, 0 Mar 26 12:01 /dev/nvidia0

Kluczowa sprawa to te dwie liczby: 195 oraz 0 . By stworzyć to urządzenie, w kontenerze wpisujemy poniższą linijkę:

root@wine32:/dev# mknod -m 666 /dev/nvidia0 c 195 0

Podobnie postępujemy dla wszystkich pozostałych urządzeń, których obsługę chcemy mieć w kontenerze, w tym przypadku są to:

root@wine32:/dev# mkdir  /dev/input
root@wine32:/dev# mknod -m 666 /dev/input/mice c 13 63
root@wine32:/dev# mknod -m 666 /dev/input/mouse0 c 13 32
root@wine32:/dev# mknod -m 666 /dev/input/event0 c 13 64
root@wine32:/dev# mknod -m 666 /dev/input/event1 c 13 65
root@wine32:/dev# mknod -m 666 /dev/input/event2 c 13 66
root@wine32:/dev# mknod -m 666 /dev/input/event3 c 13 67
root@wine32:/dev# mknod -m 666 /dev/input/event4 c 13 68
root@wine32:/dev# mknod -m 666 /dev/input/event5 c 13 69
root@wine32:/dev# mknod -m 666 /dev/input/event6 c 13 70
root@wine32:/dev# chmod 600 /dev/input/*

root@wine32:/dev# mkdir /dev/snd
root@wine32:/dev# mknod -m 666 /dev/snd/controlC0 c 116 11
root@wine32:/dev# mknod -m 666 /dev/snd/midiC0D0 c 116 2
root@wine32:/dev# mknod -m 666 /dev/snd/pcmC0D0c c 116 10
root@wine32:/dev# mknod -m 666 /dev/snd/pcmC0D0p c 116 9
root@wine32:/dev# mknod -m 666 /dev/snd/pcmC0D1c c 116 8
root@wine32:/dev# mknod -m 666 /dev/snd/pcmC0D1p c 116 7
root@wine32:/dev# mknod -m 666 /dev/snd/pcmC0D2c c 116 6
root@wine32:/dev# mknod -m 666 /dev/snd/pcmC0D2p c 116 5
root@wine32:/dev# mknod -m 666 /dev/snd/pcmC0D3c c 116 4
root@wine32:/dev# mknod -m 666 /dev/snd/pcmC0D3p c 116 3
root@wine32:/dev# mknod -m 666 /dev/snd/seq c 116 1
root@wine32:/dev# mknod -m 666 /dev/snd/timer c 116 33
root@wine32:/dev# chown root:audio /dev/snd/*

root@wine32:/dev# mknod -m 666 /dev/nvidia0 c 195 0
root@wine32:/dev# mknod -m 666 /dev/nvidiactl c 195 255

Zmieniamy hasło roota na coś innego oraz tworzymy nowego użytkownika w kontenerze i dodajemy go do grup audio oraz video:

root@wine32:~# passwd
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
root@wine32:~# adduser morfik
Adding user `morfik' ...
Adding new group `morfik' (1000) ...
Adding new user `morfik' (1000) with group `morfik' ...
Creating home directory `/home/morfik' ...
Copying files from `/etc/skel' ...
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
Changing the user information for morfik
Enter the new value, or press ENTER for the default
        Full Name []:
        Room Number []:
        Work Phone []:
        Home Phone []:
        Other []:
Is the information correct? [Y/n] y

root@wine32:~# adduser morfik video
Adding user `morfik' to group `video' ...
Adding user morfik to group video
Done.
root@wine32:~# adduser morfik audio
Adding user `morfik' to group `audio' ...
Adding user morfik to group audio
Done.

4. Połączenie z Xserverem

Podstawową konfigurację mamy z głowy. Teraz trzeba przygotować Xserver do pracy. Jeśli korzystamy z DM, czyli graficznego menadżera logowania, to trzeba poszukać w opcjach tegoż programu jak ustawić odpowiednie parametry dla procesu X . Ja odpalam Xserver przez startx (plik ~/.xinitrc), a konfigurację procesu X mam pliku w ~/.xserverrc . Na debianie Xserver startuje z opcją -nolisten tcp , co powoduje, że zdalne połączenia do tego Xservera są niemożliwe i jeśli mamy w planach korzystanie z Xservera, a mamy, to trzeba usunąć ten parametr, w moim przypadku wystarczył sam plik ~/.xserverrc zawierający:

#exec /usr/bin/X -nolisten tcp "$@"
#exec /usr/bin/X "$@"
exec /usr/bin/X "$@" -auth "$HOME/.Xauthority"

4.1. Parę słów o zabezpieczeniu Xservera

To, że zezwalamy Xserverowi na akceptowanie połączeń przez sieć, nie znaczy automatycznie, że on zacznie nawiązywać połączenia z każdym kto tego będzie chciał. Są dwa (właściwie trzy, wliczając -nolisten tcp) mechanizmy chroniące przed nieautoryzowanym dostępem do Xservera. Pierwszy z nich to xhost , drugi zaś to xauth .

xhost jest podatny na szereg ataków, np. nie rozróżnia on użytkowników zdalnych, czyli jeśli mamy adres ip i na nim wielu użytkowników, to możemy zezwolić, albo im wszystkim na połączenie, albo żadnemu. xhost nie sprawdza też czy ten kto się łączy jest tym za kogo się podaje. Jeśli jednak mamy zaufaną sieć, a tak jest w tym przypadku, możemy zezwolić adresowi ip kontenera LXC (192.168.1.2) na podłączenie się do sesji Xservera na hoście. W tym celu na głównej maszynie wydajemy poniższe polecenie:

$ xhost +192.168.1.2

Można również sprecyzować nazwę ustawioną przez plik /etc/hosts . Jeśli teraz sprawdzimy jakie hosty mogą nawiązywać połączenia z naszym Xserverem, powinniśmy ujrzeć coś podobnego do tego poniżej:

$ xhost
access control enabled, only authorized clients can connect
INET:lxc.mhouse
SI:localuser:morfik

Ustawienia nie są trwałe i znikają po zamknięciu sesji, dlatego dobrze jest dodać powyższą linijkę do autostartu na maszynie hosta.

Innym rozwiązaniem, o wiele bezpieczniejszym są ciasteczka Xservera, trzymane z reguły w katalogu użytkownika w pliku ~/.Xauthority . By skorzystać z tego mechanizmu, musimy wygenerować ciasteczko oraz odpalić proces X z opcją -auth "$HOME/.Xauthority" , tak jak jest to zrobione powyżej. Domyślnie ten plik ląduje w katalogu /tmp i jest generowany per sesja. Jeśli wskażemy ciastko Xserverowi, tylko ci klienci co mają to ciasteczko będą mogli się połączyć z naszym Xserverem.

By skorzystać z ciasteczek Xservera, musimy mieć zainstalowany w kontenerze pakiet xauth . Jeśli w grę jednak wchodzi tylko kontener LXC na lokalnej maszynie, to raczej nie potrzebujemy tego mechanizmu i nie musimy sobie nim głowy zawracać. Jeśli jednak byśmy mieli do czynienia z maszyną znajdującą się na drugim końcu wszechświata, to dobrze jest zainstalować ten pakiet:

root@wine32:~# apt-get install xauth
...
The following NEW packages will be installed:
  libx11-6 libx11-data libxau6 libxcb1 libxdmcp6 libxext6 libxmuu1 xauth
0 upgraded, 8 newly installed, 0 to remove and 0 not upgraded.
Need to get 1,049 kB of archives.
After this operation, 3,441 kB of additional disk space will be used.

Pakiet xauth jest także wymagany przy forwardingu połączeń Xservera przez ssh.

W przypadku gdybyśmy nie mieli jeszcze tego ciasteczka ($HOME/.Xauthority) możemy utworzyć je ręcznie:

$ mcookie|sed -e 's/^/add :0 . /'|xauth -q

Jednak to, że go nie mamy jest mało prawdopodobne, bo program startujący Xserver, czy to startx, czy też DM, tworzą ten plik automatycznie. Jedyny problem przed jakim teraz stoimy, to przesłanie informacji autoryzujących połączenie do Xservera na drugą maszynę. Możemy to zrobić przy pomocy ssh:

morfik:~$ xauth extract - morfikownia.mhouse:0 | ssh -x morfik@192.168.1.2 xauth merge -
morfik@192.168.1.2's password:

Zaglądnijmy teraz do pliku $HOME/.Xauthority w kontenerze:

morfik:~$ ssh morfik@192.168.1.2
morfik@192.168.1.2's password:

morfik@wine32:~$ ls -al $HOME/.Xauthority
-rw------- 1 morfik morfik 101 Mar 27 11:59 /home/morfik/.Xauthority

morfik@wine32:~$ xauth
Using authority file /home/morfik/.Xauthority
xauth> list
morfikownia.mhouse:0  MIT-MAGIC-COOKIE-1  a578170842011b9f496ab3d8d91456bc

Mechanizmy xhost oraz xauth dają nam dość spore pole manewru (szczególnie ten drugi) jeśli chodzi o to komu pozwalamy się łączyć z naszym Xserverem. Nie chronią nas jednak przed podsłuchem komunikacji.

4.2. Zmienna $DISPLAY

Dostęp do kontenera mamy zagwarantowany, jednak by moc przesłać dane do Xservera musimy wskazać systemowi gdzie ten Xserver się znajduje. Robimy to przez wyeksportowanie zmiennej $DISPLAY . Musi ona zawierać adres ip lub nazwę hosta, na którym nasłuchuje Xserver. W tym przypadku jest to 10.1.4.41 lub morfikownia.mhouse :

morfik@wine32:~$ DISPLAY="morfikownia.mhouse:0"

Oczywiście na localhoście, między interfejsem karty sieciowej i wirtualnym interfejsem naszego kontenera LXC raczej nikt podsłuchu nie zamontuje ale co w przypadku gdybyśmy pracowali zdalnie i zapragnęlibyśmy korzystać z graficznych aplikacji? Nie mówię o graniu (choć w sumie czemu by nie), tylko o np. graficznym edytorze tekstu. Moglibyśmy przesłać ruch przez ssh i zestawić okienka lokalnie, zamiast na serwerze.

4.3. Szyfrowanie połączenia do Xservera

Spróbujmy zatem na chwilę przyjąć, że nasz kontener jest gdzieś daleko i mamy do niego dostęp przez shella. Musimy skonfigurować dwa pliki od ssh -- jeden na kliencie, drugi na serwerze. Klienta możemy konfigurować albo globalnie w pliku /etc/ssh/ssh_config albo lokalnie w ~/.ssh/config . Można nawet konfigurować połączenia niezależnie i jeśli będziemy korzystać z Xservera tylko w jednym lub kilku z nich, możemy skonfigurować klienta ssh w poniższy sposób:

Host 192.168.1.2
  ForwardX11 yes
  Compression yes
  Ciphers blowfish-cbc

Zawsze możemy sprecyzować te opcje przy połączeniu, podając parametr -X dla ForwardX11, -C dla Compression, oraz -c blowfish-cbc dla Ciphers.

Na serwerze z kolei trzeba wyedytować plik /etc/ssh/sshd_config i ustawić w nim:

X11Forwarding yes
X11DisplayOffset 10

Zalogujmy się na serwer przez ssh i sprawdźmy zmienną $DISPLAY :

morfik:~$ ssh -vvv morfik@192.168.1.2
OpenSSH_6.5, OpenSSL 1.0.1f 6 Jan 2014
debug1: Reading configuration data /home/morfik/.ssh/config
debug1: /home/morfik/.ssh/config line 1: Applying options for 192.168.1.2
debug3: cipher ok: blowfish-cbc [blowfish-cbc]
debug3: ciphers ok: [blowfish-cbc]
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 19: Applying options for *
debug2: ssh_connect: needpriv 0
debug1: Connecting to 192.168.1.2 [192.168.1.2] port 22.
debug1: Connection established.
...
debug1: Next authentication method: password
morfik@192.168.1.2's password:
...
debug1: Enabling compression at level 6.
...
debug2: x11_get_proto: /usr/bin/xauth  list :0 2>/dev/null
debug1: Requesting X11 forwarding with authentication spoofing.
debug2: channel 0: request x11-req confirm 1
...
debug2: X11 forwarding request accepted on channel 0
...

morfik@wine32:~$ echo $DISPLAY
localhost:10.0

Jak widać zmienna $DISPLAY została automatycznie ustawiona, podobnie jak i plik .Xauthority i bez problemu możemy odpalać graficzne aplikacje na serwerze, a okienka zostaną przesłane do naszego lokalnego Xservera. W logu powyżej możemy również zaobserwować, że konfiguracja dla kontenera czytana jest z pliku /home/morfik/.ssh/config . Jest też włączona kompresja i wybrany szyfr blowfish-cbc oraz połączenie z Xserverem leci po ssh.

Z tego co wyczytałem na sieci, to zwykłe połączenie bez kompresji i z szyfrem AES jest bardzo wolne i nie nadaje się zbytnio do szyfrowania połączeń z Xserverem. Oczywiście można jechać na standardowych ustawieniach ssh ale wtedy, jeśli faktycznie mamy maszynę zdalną, okienka nie będą się płynnie ładować. Jeśli jednak korzystamy z LXC na lokalnej maszynie, to nie musimy ani kompresować ani szyfrować ruchu z Xserverem, co odciąży nam trochę procesor.

5. Instalacja wine i sterowników do grafiki

Przyszła pora by zainstalować wine w kontenerze:

root@wine32:~# apt-get install wine
...
The following NEW packages will be installed:
  dbus file fontconfig-config fonts-dejavu-core fonts-liberation
  libasound2 libasound2-data libasyncns0 libcap-ng0 libdbus-1-3
  libdrm-intel1 libdrm-nouveau2 libdrm-radeon1 libdrm2 libelf1
  libexif12 libexpat1 libffi6 libflac8 libfontconfig1 libfontenc1
  libfreetype6 libgcrypt11 libgd3 libgl1-mesa-dri libgl1-mesa-glx
  libglapi-mesa libglu1-mesa libgnutls26 libgpg-error0 libgphoto2-6
  libgphoto2-l10n libgphoto2-port10 libice6 libjbig0 libjpeg8
  libjson-c2 libkmod2 liblcms2-2 libldap-2.4-2 libllvm3.4 libltdl7
  libmagic1 libmpg123-0 libogg0 libopenal-data libopenal1 libp11-kit0
  libpciaccess0 libpng12-0 libpulse0 libsasl2-2 libsasl2-modules
  libsasl2-modules-db libsm6 libsndfile1 libsystemd-journal0
  libsystemd-login0 libtasn1-6 libtiff5 libtxc-dxtn-s2tc0 libudev1
  libusb-1.0-0 libvorbis0a libvorbisenc2 libvpx1 libwine
  libwine-gecko-2.21 libx11-6 libx11-data libx11-xcb1 libxau6 libxaw7
  libxcb-dri2-0 libxcb-dri3-0 libxcb-glx0 libxcb-present0 libxcb-shape0
  libxcb-sync1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxdmcp6
  libxext6 libxfixes3 libxft2 libxi6 libxinerama1 libxml2 libxmu6
  libxmuu1 libxpm4 libxrandr2 libxrender1 libxshmfence1 libxt6 libxtst6
  libxv1 libxxf86dga1 libxxf86vm1 sgml-base ucf udev wine wine32
  x11-common x11-utils xml-core
0 upgraded, 109 newly installed, 0 to remove and 8 not upgraded.
Need to get 86.9 MB of archives.
After this operation, 295 MB of additional disk space will be used.
Do you want to continue? [Y/n]

Dodatkowo jest to system 32bit osadzony wewnątrz innej maszyny 64bit, więc trzeba także doinstalować 32bitowe sterowniki dla grafiki:

root@wine32:~# apt-get install nvidia-legacy-304xx-driver
...
The following NEW packages will be installed:
  binutils cpp cpp-4.8 dkms fakeroot fontconfig gcc gcc-4.8 glx-alternative-mesa 
glx-alternative-nvidia glx-diversions hicolor-icon-theme keyboard-configuration 
kmod libasan0 libatk1.0-0 libatk1.0-data libatomic1 libavahi-client3 libavahi-common-data 
libavahi-common3 libc-dev-bin libc6-dev libcairo2 libcloog-isl4 libcups2 libdatrie1 
libegl1-mesa libegl1-mesa-drivers libfakeroot libgbm1 libgcc-4.8-dev libgcrypt20
libgdk-pixbuf2.0-0 libgdk-pixbuf2.0-common libgl1-nvidia-legacy-304xx-glx 
libglib2.0-0 libglib2.0-data libgmp10 libgomp1 libgraphite2-3 libgtk2.0-0 libgtk2.0-bin 
libgtk2.0-common libharfbuzz0b libisl10 libitm1 libjasper1 libmpc3 libmpfr4 
libopenvg1-mesa libpango-1.0-0 libpangocairo-1.0-0 libpangoft2-1.0-0 libpixman-1-0 
libpopt0 libquadmath0 libthai-data libthai0 libwayland-client0 libwayland-egl1-mesa
libwayland-server0 libxcb-render0 libxcb-shm0 libxcb-xfixes0 libxfont1 libxkbfile1 
libxvmc1 linux-compiler-gcc-4.8-x86 linux-headers-3.13-1-686-pae linux-headers-3.13-1-common 
linux-headers-686-pae linux-kbuild-3.13 linux-libc-dev make manpages manpages-dev menu
module-init-tools nvidia-installer-cleanup nvidia-kernel-common nvidia-legacy-304xx-alternative 
nvidia-legacy-304xx-driver nvidia-legacy-304xx-kernel-dkms nvidia-settings-legacy-304xx 
nvidia-support patch pkg-config shared-mime-info x11-xkb-utils xauth xfonts-base
xfonts-encodings xfonts-utils xkb-data xserver-common xserver-xorg-core 
xserver-xorg-video-nvidia-legacy-304xx
0 upgraded, 98 newly installed, 0 to remove and 5 not upgraded.
Need to get 77.7 MB of archives.
After this operation, 296 MB of additional disk space will be used.
Do you want to continue? [Y/n]

Łącznie 600MiB + system podstawowy, z tym, że instalowane z sugerowanymi pakietami. Pewnie dałoby radę jeszcze to odchudzić.

6. Dźwięk, pulseaudio i problemy z nimi związane

U mnie dźwięk na wine zawsze stwarzał problemy i nigdy mi się nie udało go skonfigurować tak by bez problemu współpracował z pulseaudio. Tak samo jest i w tym przypadku. Jeśli odpalę grę w kontenerze, to wine zajmuje urządzenie dźwiękowe i nie da rady nic odtwarzać w tym czasie na maszynie hosta. Nie wiem jak sprawa wygląda na samej alsie, bo u mnie ona też nie chce za bardzo działać w normalnych warunkach (temu używam pulse) ale myślę, że nie powinno być z nią żadnych problemów. Być może trzeba będzie doinstalować jakiś pakiet od alsy w kontenerze i coś ustawić w pliku ~/.asoundrc . Jeśli jednak odpalę wine na standardowych ustawieniach i nie będę nic odtwarzał na maszynie hosta w tym czasie, dźwięk jak najbardziej działa.

Z tego co zauważyłem, przy złej konfiguracji dźwięku proces Xorga może dość w znacznym stopniu utylizować procesor. Jedyne wyjście, w przypadku gdy nie potrafimy dojść do ładu z dźwiękiem, to wyłączyć go w wine całkowicie przy pomocy skryptu winetricks .

Jeśli znajdujemy się w sytuacji takiej jak moja i dźwięk w kontenerze zbytnio nie jest nam potrzebny, logujemy się do kontenera i pobieramy winetricks. Prawdopodobnie trzeba będzie doinstalować wget. Można oczywiście przekopiować ręcznie ten skrypt:

Debian GNU/Linux jessie/sid wine32 console

wine32 login: morfik
Password:

morfik@wine32:~$ wget http://winetricks.org/winetricks
--2014-03-27 13:13:23--  http://winetricks.org/winetricks
Resolving winetricks.org (winetricks.org)... 216.92.137.144
Connecting to winetricks.org (winetricks.org)|216.92.137.144|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 640757 (626K) [text/plain]
Saving to: ‘winetricks.1’

100%[===================================>] 640,757      447KB/s   in 1.4s

2014-03-27 13:13:24 (447 KB/s) - ‘winetricks.1’ saved [640757/640757]

morfik@wine32:~$ chmod +x winetricks

By wyłączyć dźwięk w wine wklepujemy poniższą linijkę:

morfik@wine32:~$ ./winetricks sound=disabled
Executing w_do_call sound=disabled
Executing load_sound disabled
Setting sound driver to disabled
Executing winetricks_early_wine regedit C:\windows\Temp\_sound=disabled\set-sound.reg

LXC świetnie się nadaje na sandboxa i można w nim odpalać aplikacje A/V czy też przeglądarki ale w tym celu trzeba przesłać i obraz i dźwiek przez sieć. Przesył obrazu już mamy z głowy, jak zatem przesłać dźwięk? Nie mam pojęcia czy da radę jakoś to zrobić z alsą ale pulse, można by rzec, działa OOTB, przynajmniej z grubsza.

Na serwerze, czyli maszynie hoście, musimy załadować dodatkowy moduł dla pulse i oznaczyć adresy ip jako zaufane -- te które będą mogły się łączyć z naszym serwerem dźwięku. W tym celu edytujemy plik /etc/pulse/default.pa i dopisujemy tam poniższą linijkę:

load-module module-native-protocol-tcp auth-ip-acl=192.168.1.0/24

Po tej operacji trzeba zrestartować pulse:

$ pulseaudio -k
$ pulseaudio -D

Moduły można też dodawać i usuwać przy pomocy pactl bez konieczności restartowania pulse:

$ pactl load-module module-native-protocol-tcp auth-ip-acl=192.168.1.0/24
$ pactl unload-module module-native-protocol-tcp

To cała praca jeśli chodzi o serwer pulseaudio. Na kliencie, musimy wyeksportować jedną zmienną, nie trzeba nawet tam instalować pulseaudio, przynajmniej całego. Ja po instalacji wine i sterowników do grafiki mam tylko jeden pakiet od pulse:

morfik@wine32:~$ dpkg -l | grep -i pulse
ii  libpulse0:i386       4.0-6+b1       i386         PulseAudio client libraries

I ten pakiet szuka zmiennej $PULSE_SERVER w środowisku i jeśli znajdzie, kieruje tam ruch. Trzeba tylko ją wyeksportować na zdalnej maszynie:

morfik@wine32:~$ export PULSE_SERVER=10.1.4.41

Można oczywiście ustawić tą zmienną per aplikacja:

morfik@wine32:~$ PULSE_SERVER=10.1.4.41 smplayer

6.1. Szyfrowanie dźwięku

Ruch do serwera pulseaudio nie zostanie zaszyfrowany. Jeśli chcemy mieć szyfrowany również i dźwięk, trzeba skorzystać z tcp forwardingu w ssh i przekierować pakiety, tak by szły w kanał ssh. W tym celu musimy logować się do zdalnej maszyny przez:

$ ssh -R 4713:localhost:4713 morfik@192.168.1.2

Oczywiście nie musimy tego przekierowania portów precyzować za każdym razem gdy się łączymy do serwera ssh. Wszystkie opcje połączenia możemy wpisać do pliku konfiguracyjnego klienta -- /home/morfik/.ssh/config . W tym przypadku przekierowujemy port 4713 zdalnej maszynie, zatem musimy dodać do pliku poniższa linijkę:

RemoteForward 127.0.0.1:4713 127.0.0.1:4713

Trzeba jeszcze wskazać systemowi gdzie ma szukać serwera pulseaudio -- zmienna $PULSE_SERVER ma wskazywać na lokalny port na zdalnej maszynie:

morfik@wine32:~$ export PULSE_SERVER="tcp:localhost:4713"

Teraz pakiety zamiast bezpośrednio do serwera pulse, będą szły na localhost maszyny zdalnej na port 4713 i będą forwardowane na port 4713 tunelem ssh do serwera dźwięku.

Nie ważne jakby się starać, wine i tak nie zadziała. Generalnie dźwięk jest -- amarok na hoście i smplayer w LXC odtwarzają dźwięk równolegle i nie kolidują ze sobą.

Jeśli mamy jakieś problemy z ustaleniem przyczyny problemów z forwardingiem dźwięku, pomocne mogą okazać się poniższe dwa narzędzia:

$ pax11publish
Server: {811ce104b3874ec4ec8e21d452802763}unix:/tmp/pulse-4uCknDXO1C7j/native tcp:morfikownia.mhouse:4713 tcp6:morfikownia.mhouse:4713
Cookie: c7c011a564ee890b8e2204617d9bb74392fb71571d5c51236484d72749ea67ed9b369dc02903e04
b9e272969b18b09ccd4782673719a07111c2dc7e4922e07a7e766a64f801146fd961f8186a545e5feb480e5
8e2342c80326a0c41f197842a055fa1236bd9baa2b8840c37dc2ee2d50516b5f0216a950b5762cc407a2a50
387c33d615daac6e66a613ccc2e59f9390b7d1c07a4567da7c048c2687599020872cb7009c2628ee9096239
8d71780b5182b5e1adbdfcde41401c8f996e4554b5sa7daee2e939efe1bb4401e2642599bcac65eca251f90
a76b652f17e93fc6134701a48150c77431070c132c0e5eab2ceb9ad63d186cf1b3d79fa45e41c939e1d66

lub:

$ xprop -root PULSE_SERVER
PULSE_SERVER(STRING) = "{811ce104b3874ec4ec8e21d452802763}unix:/tmp/pulse-4uCknDXO1C7j/native tcp:morfikownia.mhouse:4713 tcp6:morfikownia.mhouse:4713"

7. Test szyfrowania

Jeśli powyższe czynności były przeprowadzane na zdalnej maszynie i chcemy sprawdzić czy faktycznie cały ruch jest szyfrowany, możemy zobaczyć to przez netstart. Jeśli na adresie zdalnym będziemy mieli coś takiego jak poniżej:

root:~# netstat -tupan | grep 192
tcp        0      0 10.1.4.41:6000          192.168.1.2:60571       ESTABLISHED 21488/X
tcp        0     32 10.1.4.41:6000          192.168.1.2:60574       ESTABLISHED 21488/X
tcp        0      0 192.168.1.253:52868     192.168.1.2:22          ESTABLISHED 12839/ssh
tcp        0      0 10.1.4.41:4713          192.168.1.2:33222       ESTABLISHED 4979/pulseaudio

oznacza to, że ani połączenie z Xserverem, ani z serwerem dźwięku pulseaudio nie jest szyfrowane, czyli krótko mówiąc: obraz/dźwięk/klawiaturę/myszę można podejrzeć/podsłuchać . Jeśli byśmy przesyłali ruch zdalnie, to mamy zobaczyć, tam w logu netstat, jeden proces na porcie 22 (lub innym, ustawionym w konfiguracji ssh) i nic poza tym.

8. Problem z urxvt przy połączeniu ssh

W przypadku urxvt, ssh zachowuje się trochę dziwnie -- gdy spróbujemy odpalić graficzny program, zostanie wyświetlony taki komunikat:

'rxvt-unicode-256color': unknown terminal type.

Rozwiązaniem jest umieszczenie pliku /usr/share/terminfo/r/rxvt-unicode-256color w katalogu użytkownika, na serwerze. Do tego celu posłuży scp ale najpierw, na serwerze stwórzmy katalog /home/morfik/.terminfo/r/ . Potem przy pomocy scp kopiujemy:

morfik:~$ scp /usr/share/terminfo/r/rxvt-unicode-256color  morfik@192.168.1.2:/home/morfik/.terminfo/r/
morfik@192.168.1.2's password:
rxvt-unicode-256color                  100% 2226     2.2KB/s   00:00

I to by było na tyle. W zależności od tego czy potrzebujemy szyfrowania, trzeba korzystać z ssh, w przeciwnym wypadku, można je sobie odpuścić.

OSnews Wykop Blip Flaker Kciuk Śledzik Facebook Identi.ca Twitter del.icio.us Google Bookmarks