Safari에서 Disney+가 재생되지 않는다

Disney+에서 영화 목록들은 잘 보이다가 막상 재생을 하려니 검은색 화면이 뜨면서 아무런 동작도 하지 않았다. 이 증상을 검색해 보니 “Chrome을 쓰세요”라는 해결 방법이 많았고 실제로 그렇게 해서 동작이 되기는 했다. 다만 문제는 이 때의 해상도가 무척 낮아 보였는데, Chrome broswer에서 재공해 주는 chrome://media-internals/ 기능에 의하면 재생할 때의 해상도가 854×480 SD 화질이었다. 화질이 낮기는 해도 Chrome에서 되기는 하는 셈이다.

이런 의문이 들었다.

  1. Chrome에서는 왜 화질이 낮은가?
  2. Safari에서는 왜 재생이 안되는가?

Chrome에서 화질이 낮은 이유

Chrome에서는 DRM이 걸린 contents를 재생하기 위해 Widevine을 이용해서 decryption을 수행하는데 이 때, 고화질의 미디어를 모니터가 아닌 곳으로 빼내는 것을 막기위해서 미디어 컨탠츠 -> 메모리 -> 디스플레이 엔진 -> 케이블 -> 싱크디바이스(모니터)까지의 전과정에서 일반 메모리에 노출 없이 TEE(Trusted Execution Enivrionment)에서 이루어지면 level1으로 분류하고 이 때는 4K UHD까지의 해상도가 지원된다.

반면, TEE의 개입 없이 일반 시스템 메모리에서 decryption과 프레임 처리가 이루어지면 level3로 분류되는데 이 때는 최대 해상도가 720p로 제한된다. 이러한 DRM decryption 환경에 대한 정보는 source와 sink 디바이스간에 연결될 때 초기에 handshake 과정에서 결정 되기에 위에서 말한 디스플레이 경로상에 HDCP를 제대로 지원하지 못하는 문제가 있는 경우 480p와 같은 낮은 해상도로 재생이 이루어지게 된다.

Safari에서는 재생이 안되는 이유

DRM 걸린 컨텐츠를 재생하는 방법들 중에 Safari는 Chrome과 달리 Fairplay를 사용하는데 이 경우 재생 경로에 문제가 있으면 Widevine 처럼 해상도를 낮춰서 라도 재생하는 것이 아니라 아예 재생을 막아 버린다. 그러면 도대체 뭐가 문제여서 재생을 막아버리는 걸까?

USB3.0 HUB에 연결된 HDMI

나는 HDMI 모니터를 USB3.0 HUB를 통해서 맥북에 연결했는데, 이 경우 영상 신호가 USB로 변환되는 과정을 거쳐야 한다. 이 때 사용되는 DisplayLnik나 Silicon Motion 같은 frame을 greb, 압축해서 전달하는 메커니즘이 개입하게 되면 HDCP가 제대로 동작하지 않는다. 이 때문에 Widevine에서는 화질 저하 Fairplay에서는 재생 불가가 되는 것이다.

결론 및 해결책

가장 간단한 해결책은 HDMI cable을 맥북에 직접 연결하는 것이다. 이 경우 frame을 greb하는 경로가 없어지고, 따라서 Safari에서 Disney+가 재생되지 않는 문제가 해결된다. 또 다른 해결 방법은 frame breb 같은 방법 없이 모니터로 출력하는 DP ALT mode를 지원하는 dock 혹은 HUB 제품을 사용하는 것이다. 실제로 다른 종류의 USB type-C HUB나 HP Thunderbolt Dock G2로 확인해 보니 문제 없이 잘 재생되었다.

Ubuntu와 Arch의 동거

예전에 회사에서 하는 아나바다에서 256GB짜리 SATA SSD를 하나 만원주고 샀는데, 애초에 계획은 플레이스테이션에 넣어서 게임 로딩을 조금이라도 빠르게 해보려는 것이었지만 요즘 게임들 크기를 생각하면 몇개 들어가지도 않을것 같아서 포기하고 책상 서랍에 처박아 두었더랬다.

반면 개발용으로 회사에서 사용해 오던 워크스테이션에는 Ubuntu가 설치되어 있었는데, 이 녀석의 데이터를 최대한 보존 하면서도 내가 좋아하는 Arch Linux로 옮겨가려면 어떻게 할까 고민하다가, 기존에 쓰던 1TB Ubuntu에 추가로 256GB SSD를 설치해서 Ubuntu에서 사용하던 home directory를 그대로 Arch Linux에서 사용하겠다는 원대한 계획을 새웠다.

Arch Linux의 설치 자체는 문서가 워낙 잘 되어 있어서 그다지 어렵지는 않다. 이 번에도 큰 문제 없이 잘 설치가 되었다.

두개의 GRUB vs 하나의 GRUB

하나의 시스템에 두개의 하드를 붙여서 서로 다른 두개의 배포판을 돌리려고 하면 GRUB boot loader는 어떻게 설치해야 할까? Kernel이야 각각의 /boot 안에 넣어둔다고 해도 boot loader는 따로 두어야 하나 아니면 하나만 두어야 하나?

각각 둔다면 BIOS에서 부팅 우선순위를 정해서 어느 GRUB을 가져올지 정해서 부팅해야 한다. 따라서 원격으로 배포판을 변경하는 것이 불가능하다. 반면 한 곳에 둔다면 조금 귀찮기는 하지만 설정파일의 default 값을 설정하는 것으로 배포판을 변경하는게 불가능하지는 않다.

따라서 Ubuntu의 GRUB을 그대로 사용하고 Arch Linux를 여기에 등록하기로 했다.

Arch Linux 부팅 이미지 등록을위해 Ubuntu로 부팅해서 패키지를 깔고 probing을 수행한다.

sudo apt install os-prober

GRUB 환경설정의 GRUB_DISABLE_OS_PROBER=false는 주석처리 되어 있지만, 확실히 하려고 주석을 삭제해 주었다.

sudo emacs /etc/default/grub

# 이 부분 uncomment
GRUB_DISABLE_OS_PROBER=false

이제, 설치된 Arch linux를 마운트하고 boot image를 probing 실행 후 update-grub을 실행한다.

sudo mount /dev/sdb1 /mnt
sudo update-grub

이렇게 하면 마운트된 디스크에서 Arch Linux 부팅이미지가 검색되어 Ubuntu의 /boot/grub/grub.cfg가 update된다.

Default booting 변경하기

이제 부팅할 때마다 Arch Linux가 기본으로 선택되도록 GRUB을 변경한다.

/boot/grub/grub.cfg에서 menuentry 와 submenu 구문을 검색해 본다.

❯❯sudo grep -E "^menuentry|^submenu" grub.cfg

menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu --class os
submenu 'Advanced options for Ubuntu' $menuentry_id_opt
menuentry "Memory test (memtest86+x64.bin)" --class memtest
menuentry 'Memory test (memtest86+x64.bin, serial console)' --class memtest
menuentry 'Arch Linux (on /dev/sdb1)' --class arch --class gnu-linux --class gnu
submenu 'Advanced options for Arch Linux (on /dev/sdb1)' $menuentry_id_option

순서상 Arch linux는 다섯번 째 위치에 있으므로 0부터 시작해서 4번이 Arch를 시작하는 entry가 된다.

/etc/default/grub의 GRUB_DEFAULT를 4로 변경하고 update-grub을 수행한 다음 리붓한다.

GRUB_DEFAULT=4

부팅하기 전에 update-grub에 의해 default entry가 제대로 설정되었는지 “set default=” 구문을 검색해 보면 확인할 수 있다.

❯❯❯ sudo grep "set default=" /boot/grub/grub.cfg
   set default="${next_entry}"
   set default="4"

Home directory mount

새롭게 home directory를 생성하는 것보다 기존에 Ubuntu에서 사용하던 것을 그대로 가져오면 local 설정들을 별 힘들이지 않고 재 사용할 수 있다.

먼저 Ubuntu partition을 /mnt/ubuntu에 마운트 해둔다.

그리고 나서 genfstab -U /mnt/ubuntu 명령어로 partition의 UUID를 생성하고 /etc/fstab에 등록해 준다.

마지막으로 생성된 home directory를 삭제하고 /mnt/ubunut/home안에 있는 홈디렉토리로 symbolic link를 생성하고 필요에 다라 소유권을 설정해 준다.

업데이트 문제는 없나?

이 상의 설정은 Ubuntu와 Arch Linux 각각이 /boot 디렉토리를 가지고 있고, grub은 Ubuntu의 /boot/grub 안에 있는 것을 사용하는 형태이다. 따라서 각 distro의 커널들을 자신의 /boot 디렉토리에 위치하기 때문에 커널 버전 업데이트는 문제가 되지 않는다. 다만, grub 자체의 버전이나 설정을 변경하고 싶다면 반드시 Ubuntu로 부팅해야만 한다.

무작위적인 WordPress 비밀번호 초기화 요청 막기

최근들어 며칠간 하루에 꼭 세번씩 비밀번호 초기화 요청이 날아들고 있다. 이후에 다른 동작이 없으니 그냥 두어도 괜찮겠지만 아무래도 bot으로 WordPress의 wp-login.php에 계속 자동요청을 해대는게 영 찜찜하다. 아마도 다음과 비슷한 명령어를 스크립팅하는 모양이다.

curl -X POST "https://litcoder.com/wp-login.php?action=lostpassword" \
     -d "user_login=litcoder" \
     -d "wp-submit=New Password"

막는 방법이 없을까 해서 찾아보니 reCAPCHA나 CloudFlare의 Turnstile같은 서비스를 무료로 설정하는 방법들이 있었지만 회원가입이나 사이트를 연결하는 설정을 해야 하는게 부담됐다. 그래서 마땅한 플러그인이 없는지 뒤져 봤더니 이런 종류의 공격을 막는 플러그인들이 몇몇 있었다. 그 중에서도 로그인을 페이지를 다른 URL로 변경하고 비정상적인 admin 요청을 모두 home page로 돌려보내는 참신해보이는 플러그인이 하나 있어서 설정해 주었다. 만약 새로 변경하는 URL이 노출된다고 해도 바꿔버리는 것으로 쉽게 다시 무력화 시킬 수 있을테니까 말이다.

로그인 용으로 사용할 URL을 정의해 주면 해당 URL로 연결할 때만 로그인 페이지가 열리고 나머지 경우에는 다른페이지로 리다이렉트된다. URL 자체 뿐만 아니라 사이트의 로그인 기능에 대한 URL도 함께 변경되어 쉽게 사용할 수 있었다.

결론

설정 이후 며칠간 지켜봤는데 악의적인 비밀번호 초기화 요청들은 더 이상 들어오고 있지는 않다. 그런데 최근에는 또 다른 종류의 SQL injection을 시도하는 댓글들이 스팸함에 들어오기 시작했다. 플러그인 설치한 시점과 미묘하게 겹치는게 좀 이상하다는 지나친(!) 생각이 잠깐 스치기는 했지만, 좀 과한것 같긴하다. 여튼 이것들은 일단 스팸으로 걸러지고 있으니 좀 더 두고 보자.