어제는 간만에 Flutter로 코드 만들어 보느라 고생했고 오늘은 또 간만에 파이썬으로 코드 만드느라 고생중입니다. 그 사이에 무슨 일이 있었던 것인가... (빅써 없데이트!?) 갑작스레 새 코드 프로젝트를 만들기 위해 virtualenv 를 돌리는데 Library not loaded 에러가 발생합니다. 자연스레 pip 로 virtualenv 를 업데이트 해볼랬더니.. 어라? pip도 동작을 안합니다.
% virtualenv
dyld: Library not loaded: /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
Referenced from: /Library/Frameworks/Python.framework/Versions/3.6/Resources/Python.app/Contents/MacOS/Python
Reason: image not found
zsh: abort virtualenv
% pip
dyld: Library not loaded: /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
Referenced from: /Library/Frameworks/Python.framework/Versions/3.6/Resources/Python.app/Contents/MacOS/Python
Reason: image not found
zsh: abort pip
뭔가 운영체제 업데이트가 되면서 symlink가 꼬인 느낌이라 과감히 brew 로 파이썬을 재설치 해보기로 했습니다. 의존성이 걸려있는 패키지가 너무 많은 탓인지 업데이트가 한참 걸리네요. 열심히 다운로드 받고 설치하는 동안 저의 맥북프로는 또 다시 이륙을 하고는 고도를 높여 나갑니다...
% brew install python
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 3 taps (homebrew/core, homebrew/cask and homebrew/services).
...
...
==> Installing python@3.9
==> Pouring python@3.9-3.9.2_1.big_sur.bottle.tar.gz
Error: The `brew link` step did not complete successfully
The formula built, but is not symlinked into /usr/local
Could not symlink bin/2to3
Target /usr/local/bin/2to3
already exists. You may want to remove it:
rm '/usr/local/bin/2to3'
To force the link and overwrite all conflicting files:
brew link --overwrite python@3.9
To list all files that would be deleted:
brew link --overwrite --dry-run python@3.9
Possible conflicting files are:
/usr/local/bin/2to3 -> /Library/Frameworks/Python.framework/Versions/3.6/bin/2to3
/usr/local/bin/easy_install-3.9 -> /usr/local/Cellar/python@3.9/3.9.0_2/bin/easy_install-3.9
/usr/local/bin/idle3 -> /Library/Frameworks/Python.framework/Versions/3.6/bin/idle3
/usr/local/bin/pip3 -> /usr/local/Cellar/python@3.9/3.9.0_2/bin/pip3
/usr/local/bin/pip3.9 -> /usr/local/Cellar/python@3.9/3.9.0_2/bin/pip3.9
/usr/local/bin/pydoc3 -> /Library/Frameworks/Python.framework/Versions/3.6/bin/pydoc3
/usr/local/bin/python3 -> /Library/Frameworks/Python.framework/Versions/3.6/bin/python3
/usr/local/bin/python3-config -> /Library/Frameworks/Python.framework/Versions/3.6/bin/python3-config
==> /usr/local/Cellar/python@3.9/3.9.2_1/bin/python3 -m ensurepip
==> /usr/local/Cellar/python@3.9/3.9.2_1/bin/pip3 install -v --global-option=--no-user-cfg --install-option=--force --install-option=--single-version-externally-managed --install-option=--record=installed.txt --upgrade --target=/usr/local
==> /usr/local/Cellar/python@3.9/3.9.2_1/bin/pip3 wheel --wheel-dir=/usr/local/Cellar/python@3.9/3.9.2_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/ensurepip/_bundled /usr/local/Cellar/python@3.9/3.9.2_1/libexec/setuptools /us
==> Caveats
Python has been installed as
/usr/local/bin/python3
Unversioned symlinks `python`, `python-config`, `pip` etc. pointing to
`python3`, `python3-config`, `pip3` etc., respectively, have been installed into
/usr/local/opt/python@3.9/libexec/bin
You can install Python packages with
pip3 install <package>
They will install into the site-package directory
/usr/local/lib/python3.9/site-packages
See: https://docs.brew.sh/Homebrew-and-Python
...
...
brew 의 로그를 보면서 에러가 난 부분들을 집중적으로 살펴봅니다. 확실히 python 3.9 를 설치하는 과정에 link 연결이 원활하지 않은게 보이는군요. 설치가 끝나는대로 brew link 명령을 --overwrite 으로 돌려보는게 좋을 것 같습니다. 그나저나 설치는 언제 끝나나...하고 있는데 설치가 끝났네요. 자 한번 brew link 를 수행해 보겠습니다
% brew link --overwrite --dry-run python@3.9
Would remove:
/usr/local/bin/2to3 -> /Library/Frameworks/Python.framework/Versions/3.6/bin/2to3
/usr/local/bin/idle3 -> /Library/Frameworks/Python.framework/Versions/3.6/bin/idle3
/usr/local/bin/pydoc3 -> /Library/Frameworks/Python.framework/Versions/3.6/bin/pydoc3
/usr/local/bin/python3 -> /Library/Frameworks/Python.framework/Versions/3.6/bin/python3
/usr/local/bin/python3-config -> /Library/Frameworks/Python.framework/Versions/3.6/bin/python3-config
%
% brew link --overwrite python@3.9
Linking /usr/local/Cellar/python@3.9/3.9.2_1... 21 symlinks created.
%
%
% python3
dyld: Library not loaded: /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
Referenced from: /Library/Frameworks/Python.framework/Versions/3.6/Resources/Python.app/Contents/MacOS/Python
Reason: image not found
zsh: abort python3
하아... 뭔가 잘 해결이 안되었네요. 분명 3.9 를 설치한 것 같은데 에러 내용의 중간을 보니 3.6을 참조하고 있는게 문제인 듯 싶습니다. python3 의 위치를 탐색해서 강제로 symlink 를 변경해 보도록 하겠습니다.
% which python3
/Library/Frameworks/Python.framework/Versions/3.6/bin/python3
%
%
% cd /Library/Frameworks/Python.framework/Versions/3.6/bin/
% rm python3
%
% which python3
/usr/local/bin/python3
하하~! 요렇게 하니 이제 새로 설치된 python3.9 버전이 잘 실행이 됩니다. 그러나 여전히 virtualenv나 pip가 제대로 동작하지 않았습니다.
% python3
Python 3.9.2 (default, Feb 24 2021, 13:26:09)
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
뭐가 문제일까 하다가... 문득 .bash_profile 에 추가되었던 PATH 정보가 생각나서 이 부분을 모두 제거하고 터미널을 재기동 해보았습니다. 그리고 나니 이제야 제대로 동작하는 virtualenv 와 pip..!! 개발 환경은 처음 설정이 늘 어렵고 번거롭고... 그런것 같습니다 ㅎㅎ. 비슷한 어려움을 겪는 분들이 계시다면,
1. brew 로 python 패키지 업데이트 2. symlink 가 이상하게 걸려 있는지 확인 3. PATH 로 잡혀 있는 파이썬 디렉토리 확인
VPN 서버를 로컬에서 운영해야 하는 이유는 (굳이 찾자면) 여러가지가 있겠습니다만, 개인적으로는 OpenVPN Web UI 의 커스터마이징을 위하여 개발 환경이 필요했던 관계로 설치를 하게 되었습니다. 리눅스 환경에서의 설치는 종종 해왔지만 막상 로컬 Mac 환경에 설치하려니 적당한 가이드가 없는 것 같아 시행착오를 하며 내용을 정리해 봅니다.
Homebrew 를 이용한 패키지 설치
OpenVPN 커뮤니티 버전의 소스를 다운로드 받아 빌드를 할까? 하는 생각을 0.1 초간 한 뒤에 바로 Homebrew 로 돌아섰습니다. 안그래도 지저분한 로컬 환경이라 (도커에 익숙치가 않네요) 더 지저분하게 하지 말자는 생각이 있었고, 빌드 환경 설정에 시간을 쓰지 말자는 생각이 들어 Homebrew 를 사용해 봤습니다.
매번 느끼는 거지만 Homebrew 는 업데이트가 너무 많습니다. 따로 패키지만 올리고 싶어도 일단 필요한 것들을 다 업데이트 하고 시작하니 시간이 꽤 걸립니다. 한참을 지나 OpenVPN 설치를 위해 Dependency 가 있는 패키지들을 쭈욱~ 설치해 나갑니다. 중간에 몇 가지 심볼릭링크 작업이 에러가 나는게 보였지만... 잘 되리라 믿고 진행을 계속 했습니다. 한 15분정도 걸렸던 것 같네요.
OpenVPN 바이너리는 어디에!?
리눅스 환경에서는 패키지로 공급되는 프로그램들은 대부분 데몬 설정 파일도 같이 올려주고 systemctl 이나 services 명령으로 활성화, 비활성화를 쉽게 할 수 있습니다. 막상 Mac 환경에서는 그렇게 쓰는 경우가 잘 없다보니... 설치후에 무얼 해야하나 5분정도 멍을 때렸습니다. 이단 설치경로를 찾아보기로 했고 아래의 경로에서 OpenVPN 을 찾을 수 있었습니다.
OpenVPN 이 설치되고나면 설정파일은 아래의 경로에서 찾아볼 수 있습니다. client.conf 는 VPN 의 클라이언트가 될 머신에서 사용하는 파일이고 server.conf 는 VPN 서버에서 사용하는 파일입니다. 리눅스에서도 그랬었나 기억이 좀 가물거립니다만 친절한 안내 멘트가 같이 들어 있으니 익숙하지 않은 경우에는 내용을 하나씩 살펴보는 것을 추천드립니다. (라고 적으면서 저도 잘 모르는 내용이 많긴 합니다만... ㅎ)
우리는 성미가 급하니 OpenVPN 이 동작하는지 한번 보겠습니다. 느낌적 느낌으로 인지하셨겠지만 당연히 동작하지 않습니다 ㅎㅎ. 필요한 키생성 등을 해야 하지만 그래도 한번 실행해 보는 맛이 있어야겠죠?
% /usr/local/opt/openvpn/sbin/openvpn --config ./server.conf
2020-12-01 13:06:17 WARNING: --topology net30 support for server configs with IPv4 pools will be removed in a future release. Please migrate to --topology subnet as soon as possible.
2020-12-01 13:06:17 DEPRECATED OPTION: --cipher set to 'AES-256-CBC' but missing in --data-ciphers (AES-256-GCM:AES-128-GCM). Future OpenVPN version will ignore --cipher for cipher negotiations. Add 'AES-256-CBC' to --data-ciphers or change --cipher 'AES-256-CBC' to --data-ciphers-fallback 'AES-256-CBC' to silence this warning.
2020-12-01 13:06:17 Cannot pre-load tls-auth keyfile (ta.key)
2020-12-01 13:06:17 Exiting due to fatal error
네, 그렇습니다. 치명적인 에러가 있어서 실행이 안되었네요 ㅎㅎ DEPRECATED 로 표시된 부분은 server.conf 를 열어서 AES-256-CBC 로 된 부분을 AES-256-GCM 으로 변경해주시면 됩니다. 자, 그러면 tls-auth 를 위한 ta.key 파일을 생성해 보겠습니다
각종 Key 생성하기
우선 OpenVPN 용 Static Key 를 만들겠습니다. tls-auth 용 키이며 ta.key 파일을 만들어야 하는데요, 생성하는 것은 간단합니다. OpenVPN 바이너리에서 옵션을 지정하여 키를 생성할 수 있습니다. Configuration 파일이 위치한 경로에 ta.key 파일도 만들어 보도록 하겠습니다.
이제 easyrsa 를 이용하여 PKI 를 구성하기 위한 루트 인증서, 비밀키, 공개키 등을 만들도록 하겠습니다. 참고로, easyrsa 는 기본 값으로 /usr/local/etc/pki 경로에 파일들을 생성하도록 되어 있으니 참고하시기 바랍니다.
//===============================
// 초기화
//===============================
% /usr/local/opt/easy-rsa/bin/easyrsa init-pki
init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /usr/local/etc/pki
//===============================
// CA Certificate 생성
//===============================
% /usr/local/opt/easy-rsa/bin/easyrsa build-ca nopass
Using SSL: /usr/local/opt/openssl@1.1/bin/openssl OpenSSL 1.1.1h 22 Sep 2020
Generating RSA private key, 2048 bit long modulus (2 primes)
..........................................................................................................................................+++++
..............................................................................................+++++
e is 65537 (0x010001)
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:MyLocalPKI
CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/usr/local/etc/pki/ca.crt
//===============================
// 서버인증서 생성
//===============================
% /usr/local/opt/easy-rsa/bin/easyrsa build-server-full server nopass
Using SSL: /usr/local/opt/openssl@1.1/bin/openssl OpenSSL 1.1.1h 22 Sep 2020
Generating a RSA private key
..............................................................+++++
...................................................................................................................................+++++
writing new private key to '/usr/local/etc/pki/easy-rsa-38744.0yXv0x/tmp.Xb7eac'
-----
Using configuration from /usr/local/etc/pki/easy-rsa-38744.0yXv0x/tmp.VwN7Lb
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName :ASN.1 12:'server'
Certificate is to be certified until Mar 6 04:45:50 2023 GMT (825 days)
Write out database with 1 new entries
Data Base Updated
//===============================
// 키 교환 알고리즘용 DH 파라메터 생성
//===============================
% /usr/local/opt/easy-rsa/bin/easyrsa gen-dh
Using SSL: /usr/local/opt/openssl@1.1/bin/openssl OpenSSL 1.1.1h 22 Sep 2020
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time
...............................................................................+....................................+......................................
DH parameters of size 2048 created at /usr/local/etc/pki/dh.pem
//===============================
// Client 용 Credential 생성
//===============================
% /usr/local/opt/easy-rsa/bin/easyrsa build-client-full mylocalclient nopass
Using SSL: /usr/local/opt/openssl@1.1/bin/openssl OpenSSL 1.1.1h 22 Sep 2020
Generating a RSA private key
...................................................................................................................................................................+++++
..+++++
writing new private key to '/usr/local/etc/pki/easy-rsa-65143.eROtDW/tmp.haeAUt'
-----
Using configuration from /usr/local/etc/pki/easy-rsa-65143.eROtDW/tmp.EOABaH
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName :ASN.1 12:'mylocalclient'
Certificate is to be certified until Mar 6 05:03:20 2023 GMT (825 days)
Write out database with 1 new entries
Data Base Updated
뭔가 번잡하니 많네요. 하지만 늘 하던 일이었다고 생각하시고 하나씩 따라오셨으면 특별히 문제 없었을 겁니다. 이제 생성된 파일들이 어디에 있는지 체크해 볼까요?
// 서버 인증서와 클라이언트 인증서
% pwd
/usr/local/etc/pki/issued
% ls
mylocalclient.crt server.crt
// 서버 비밀키와 클라이언트 비밀키, CA 비밀키
% pwd
/usr/local/etc/pki/private
% ls
ca.key mylocalclient.key server.key
// CA 인증서와 DH 파라메터 파일
% pwd
/usr/local/etc/pki
% ls ca.crt dh.pem
ca.crt dh.pem
3가지 경로에 각 파일이 나뉘어져 있습니다. easyrsa 는 마치 내가 CA (Certificate Authority) 가 된 것처럼 인증서를 발급해주고 만들어주는 역할을 한 것이고, 각각 필요한 키들이 생성되어 적절한 위치에 나뉘어져 있다고 생각하시면 됩니다.
OpenVPN 서버를 다시 시작해보자
간결한 관리를 위하여 앞서 작업했던 /usr/local/etc/openvpn 경로로 서버에서 필요한 파일들을 복사해 오도록 하겠습니다. 파일명은 server.conf 에 기술된 파일명을 기준으로 하고 있으니, 다른 이름으로 생성했다면 이름을 변경해 주셔도 무방합니다. 파일이 복사되었다면 sudo 권한으로 OpenVPN 서버를 실행하도록 하겠습니다.
% pwd
/usr/local/etc/openvpn
% cp /usr/local/etc/pki/ca.crt .
% cp /usr/local/etc/pki/dh.pem ./dh2048.pem
% cp /usr/local/etc/pki/private/server.key .
% cp /usr/local/etc/pki/issued/server.crt .
% sudo /usr/local/opt/openvpn/sbin/openvpn --config ./server.conf
% sudo /usr/local/opt/openvpn/sbin/openvpn --config ./server.conf
2020-12-01 14:17:01 WARNING: --topology net30 support for server configs with IPv4 pools will be removed in a future release. Please migrate to --topology subnet as soon as possible.
2020-12-01 14:17:01 OpenVPN 2.5.0 x86_64-apple-darwin19.6.0 [SSL (OpenSSL)] [LZO] [LZ4] [PKCS11] [MH/RECVDA] [AEAD] built on Nov 13 2020
2020-12-01 14:17:01 library versions: OpenSSL 1.1.1h 22 Sep 2020, LZO 2.10
2020-12-01 14:17:01 NOTE: your local LAN uses the extremely common subnet address 192.168.0.x or 192.168.1.x. Be aware that this might create routing conflicts if you connect to the VPN server from public locations such as internet cafes that use the same subnet.
2020-12-01 14:17:01 Diffie-Hellman initialized with 2048 bit key
2020-12-01 14:17:01 Outgoing Control Channel Authentication: Using 160 bit message hash 'SHA1' for HMAC authentication
2020-12-01 14:17:01 Incoming Control Channel Authentication: Using 160 bit message hash 'SHA1' for HMAC authentication
2020-12-01 14:17:01 Opened utun device utun7
2020-12-01 14:17:01 /sbin/ifconfig utun7 delete
ifconfig: ioctl (SIOCDIFADDR): Can't assign requested address
2020-12-01 14:17:01 NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure
2020-12-01 14:17:01 /sbin/ifconfig utun7 10.8.0.1 10.8.0.2 mtu 1500 netmask 255.255.255.255 up
2020-12-01 14:17:01 /sbin/route add -net 10.8.0.0 10.8.0.2 255.255.255.0
add net 10.8.0.0: gateway 10.8.0.2
2020-12-01 14:17:01 Could not determine IPv4/IPv6 protocol. Using AF_INET6
2020-12-01 14:17:01 Socket Buffers: R=[786896->786896] S=[9216->9216]
2020-12-01 14:17:01 setsockopt(IPV6_V6ONLY=0)
2020-12-01 14:17:01 UDPv6 link local (bound): [AF_INET6][undef]:1194
2020-12-01 14:17:01 UDPv6 link remote: [AF_UNSPEC]
2020-12-01 14:17:01 MULTI: multi_init called, r=256 v=256
2020-12-01 14:17:01 IFCONFIG POOL IPv4: base=10.8.0.4 size=62
2020-12-01 14:17:01 IFCONFIG POOL LIST
2020-12-01 14:17:01 Initialization Sequence Completed
이렇게 대략 로컬 설치가 끝났습니다. 사실 이후에 udp4 만 사용하도록 변경, tls-auth 가 생각처럼 동작하지 않아 제외하는 작업등을 진행한 후에 정상적인 기동, 클라이언트의 연결이 가능했습니다. 이 부분은 다음 포스팅에서 정리해 보도록 하겠습니다.
개발을 하던 다른 트러블 슈팅을 하던 기기의 패킷을 추출해야 하는 경우들이 많습니다. Mac 을 사용하는 경우 rvi (Remote Virtual Interface) 를 이용해서 기기를 연결해서 쉽게 와이어샤크 Wireshark 같은 물건으로 패킷을 캡쳐할 수 있습니다. 말 그대로 가상 네트워크 인터페이스로 iOS 기기를 설정하기 때문이죠.
개인 기기로 아이폰을 쓰다보니 안드로이드는 샤오미 홍미4 이후 오랫동안 쓴적이 없었습니다. 그나마도 딸래미 학교 들어가면서 비상연락용 겸 슬랙 서적 집필할 때 화면 캡쳐용으로 산거라 진심을 다해서 써보지도 않았습니다. 허나, 현실에서는 안드로이드 환경의 패킷을 확인해야 하는 일이 종종 생기더군요.
ADB, 안드로이드 디버그 브릿지
이유는 잘 기억나지 않지만 제 Mac 에도 안드로이드 스튜디오 Android Studio 가 이미 설치되어 있었습니다 -_-;; ADB 는 Android SDK 개별 설치 혹은 Android Studio 설치시 SDK 에 딸려서 설치되는 디버그 툴인 것 같습니다. 패킷을 뜨기 위해 기기를 연결할 필요가 있었는데요 (사실은 NOX 로 기기 에뮬레이션해서...) ADB 가 그냥 실행이 되지는 않더군요.
Android Studio 가 정상적으로 동작하고 있다면 ADB 는 어딘가에 잘 설치가 되어 있는 상태입니다. 제 경우에는 아래의 Path 에서 해당 내용을 확인할 수 있었습니다. 아마 대부분 비슷한 경로에 설치가 되어 있을거라 생각합니다.
보통 SDK 가 설치되면 자동으로 Path 를 설정해 주는 경우가 많은데 Android SDK 는 그러지 않나 봅니다. (혹은 제가 영어로 나온 메세지를 놓친 것일지도... 속닥속닥...) 제 경우 필요한 터미널 환경 설정을 .bash_profile 에 하고 있어서 아래와 같이 Android SDK 의 Platform tools 경로를 추가해 주었습니다. 이렇게 해주고나니 어디서든 adb 를 실행할 수 있게 되었습니다 ㅎㅎ
# Setting PATH for Android Platform Tools (esp for ADB)
export PATH="${PATH}:/Users/nopd/Library/Android/sdk/platform-tools"
델리미터(Delimeter)로 컬럼이 분기된 텍스트 파일(보통은 아마도 로그파일)을 다룰 때 가장 손쉽게 쓸 수 있는 도구가 awk 입니다. 물론 cut 등도 있지만 탐색한 이후의 작업을 주로 담당하기 때문에 탐색시에는 역시 awk 가 대세입니다. (당연하겠습니다만 awk 도 정규표현식의 탐색을 지원하니 자세한 내용은 글 맨 아래의 포스팅 링크를 참고해 주세요.)
awk 에서 특정 컬럼의 값을 표현하는 방법은 여러가지가 있습니다. 그동안 많이 사용했던 패턴은 아래와 같은 방식이었는데요 이 방식에서도 or, and 조건을 사용하는 것은 문제가 없습니다. 조건을 동시에 만족하는 경우를 찾으려면 &&를, 어느 하나라도 만족하는 경우를 찾으려면 || 를 사용하면 됩니다.
// AND (&&)
awk '$2=="" && $19~/my/ {print $11, $24}'
// OR (||)
awk '$2=="" || $19~/my/ {print $11, $24}'
이렇게 표현하는 방법 외에 브레이스({}) 안쪽에 if 문을 넣어서 사용하는 방법도 있습니다. 어느 방법을 사용하던 원하는 결과를 얻을 수 있으면 되니 손에 더 감기는 방법을 사용하면 좋을 것 같습니다. NOT 조건을 사용하는 경우에도 보다 쉽고 명시적으로 읽을 수 있어서 이 방법을 개인적으로는 선호하는 편입니다.
// AND (&&)
awk '{ if ($5=="/test" && $3=="GET") print $0}'
// OR (&&)
awk '{ if ($5=="/test" || $5=="/beta") print $0}'
// NOT (!)
awk '{ if (!($5=="/test")) print $0}'