はじめに
とあるプロキシでは、HTTPS通信の中身を見たいという理由で、Basic認証proxyに加えて、proxyの認証局のCA証明書(SSLサーバ証明書)を加えないと,HTTP/HTTPS通信できないものが存在する。
HTTPS通信が行われるアプリ、ライブラリを使用する際は追加設定を行う必要があり、なんともブラウザファーストな厄介な代物である。
この仕組みでは、proxyがサイトのURLごとにCA証明書を作成し、pcとproxy間、proxyとサーバ間でSSL/TLS通信を確立する。
用語
- プロキシユーザー名: プロキシサーバでBasic認証が必要な時のユーザー名
- プロキシパスワード: プロキシサーバでBasic認証が必要な時のパスワード
- CAfile: 使用するCA証明書ファイル
- CApath: CA証明書が格納されたディレクトリ。CA証明書(もしくは証明書へのシンボリックリンク)は{hash}.0という名前になっている必要がある。 {hash}は証明書のsubjectをハッシュ化した値。
openssl x509 -in <証明書ファイル> -hash -noout
で確認できる。 - デフォルト値: アプリやライブラリで指定するCAfileやCApathの初期値。コンパイル時の設定やディストリビューションにより異なっている。
ハッシュの作成方法は以下のように行う。
$ openssl x509 -in IIJ1.pem -hash -noout
ec922ae0
$ sudo cp IIJ1.pem /etc/ssl/certs
$ cd /etc/ssl/certs
$ sudo ln -s IIJ1.pem $(openssl x509 -in IIJ1.pem -hash -noout).0
CA証明書のエンコードの種類
ASN.1というデータ構造で格納されており
- DER形式: バイナリ形式でエンコード
- PEM形式: BASE64方式でエンコード
詳しくはこちらhttps://qiita.com/TakahikoKawasaki/items/4c35ac38c52978805c69
curlとかLinuxで使用されているコマンドはほとんどがPEM形式のCA証明書で認識されている。DER形式のものはPEM形式に変更する必要がある。
DER形式からPEM形式に変更
openssl x509 -inform der -in IIJ1.der -outform pem -out IIJ1.pem
ディストリビューションのCA証明書の追加
ca-certificates
という大変ありがたい仕組みが用意されている。
Ubuntu22.04の場合
元がIIJ1.derというDER形式のファイルの場合
$ sudo apt install ca-certificates
$ ls
IIJ1.der
$ openssl x509 -inform der -in IIJ1.der -outform pem -out IIJ1.crt
$ sudo cp IIJ1.crt /usr/local/share/ca-certificates
$ sudo update-ca-certificates
※拡張子を.crtにすること
CAfile: /etc/ssl/certs/ca-certificates.crt
とCApath: /etc/ssl/certs
の中身が更新される。
CentOS Stream9の場合
元がIIJ1.derというDER形式のファイルの場合
rootディレクトリにファイルおいて実行
dnf install ca-certificates
openssl x509 -inform der -in IIJ1.der -outform pem -out IIJ1.pem
cp IIJ1.pem /usr/share/pki/ca-trust-source/anchors/
update-ca-trust
CAfile: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
が更新される。
(curlはCAfileとして/etc/pki/tls/certs/ca-bundle.crt
を指定しているが、実態はそのシンボリックリンクになっている。)
ディストリビューション側で管理しているプログラムライブラリではシンボリックリンクを張ったりしてこのCA証明書を利用する設計になっているはずである。
※このようにありがたい仕組みあるので、間違ってもCA証明書の検証を無視する設定にしないこと。
方針
設定の優先度は
コマンドのオプション>プログラム毎の設定ファイル>環境変数>デフォルト値
となっていることが多い。
プロキシ認証関係
プロキシサーバのBasic認証はセキュリティ上の問題からコマンドのオプションはできるだけ使用せずにおこなう。プログラム毎の設定ファイル又は環境変数により設定する。
CA証明書関係
基本デフォルト値で行う。ディストリビューションのCA証明書の追加でカバーが可能ならそれで行う。
curlによる設定
CA証明書はPEM形式でないと読み込めない。
コマンドオプションによる設定
curl \
--proxy <protocol://host:port> \
--noproxy <no-proxy-list> \
--proxy-user <user:password> \
--cacert <file> \
--capath <dir> \
設定ファイル
proxy = "<protocol://host:port>"
noproxy = "<no-proxy-list>"
proxy-user = "<user:password>"
cacert = "<file>"
capath = "<dir>"
環境変数
- http_proxy=protocol://[user:passwd@]host:port
- HTTPS_PROXY=protocol://[user:passwd@]host:port
- https_proxy=protocol://[user:passwd@]host:port
- ALL_PROXY
- NO_PROXY
- no_proxy
- CURL_CA_BUNDLE: CA証明書のパス
※http_prxoyは小文字のみ、それ以外は両方OK。両方ある場合は小文字優先。
CA証明書のデフォルト値
curl --verbose
で調べることが可能。
Ubuntu22.04でのCA証明書のデフォルト値
ca-certificatesパッケージのデータを利用している。
- CAfile:
/etc/ssl/certs/ca-certificates.crt
- CApath:
/etc/ssl/certs
基本はca-certificates
の仕組みで組み込めばよい。
CentOS Stream 9でのCA証明書のデフォルト値
ca-certificatesパッケージのデータを利用している。
- CAfile:
/etc/pki/tls/certs/ca-bundle.crt
- CApath: None
基本はca-certificates
の仕組みで組み込めばよい。
dnfの設定
http/https通信はcurlを利用。
設定ファイル
dnfは内部でcurlを用いているが、~/.curlrc
読み込まない。
[main]
proxy=<protocol://host:port>
proxy_username=<user>
proxy_password=<password>
sslcacert=<pemfile>
環境変数
curlの使っているのでcurlと同じ
dnfのデフォルト値
curlのデフォルト値となるが
- CAfile:
/etc/pki/tls/certs/ca-bundle.crt
- CApath: None
基本はca-certificates
の仕組みで組みこんでsslcacert
は使わない。
aptの設定
コマンドオプションによる設定
sudo apt \
-o Acquire::http::Proxy=<http_proxy> \
-o Acquire::https::Proxy=<https_proxy>
設定ファイル
/etc/apt/apt.conf
または/etc/apt/apt.conf.d
以下で作成したXXXX.conf
ファイルに記載
Acquire {
http {
Proxy "http://[[user][:pass]@]host[:port]/";
};
https {
proxy "http://[[user][:pass]@]host[:port]/";
};
};
環境変数による設定
- http_proxy=protocol://[user:passwd@]host:port
- https_proxy=protocol://[user:passwd@]host:port
※sudo実行する場合はデォルトではこの環境変数は削除されて実行してしまうので、sudoの設定ファイルを変更してください。
Defaults env_keep = "http_proxy https_proxy"
デフォルトのCA証明書について
CApath:/etc/ssl/certs
CAfile:/etc/ssl/certs/ca-certificates.crt
基本はca-certificates
の仕組みで組み込めばよい。
Python pipの設定
コマンドオプションによる設定
python -m pip --proxy <protocol://[user:passwd@]host:port> \
--cert <file.pem>
設定ファイル
コマンドでpip.confに書き込む
python -m pip config set global.proxy <protocol://[user:passwd@]host:port>
python -m pip config set global.cert <file.pem>
直接書き込む
[global]
proxy = <protocol://[user:passwd@]host:port>
cert = <file.pem>
ちなみにLinuxでの場所は
$HOME/.config/pip/pip.conf($XDG_CONFIG_HOME=$HOME/.config)
Windowsでの場所は%APPDATA%\pip\pip.ini
環境変数による設定
- http_proxy=protocol://[user:passwd@]host:port
- https_proxy=protocol://[user:passwd@]host:port
- no_proxy
- PIP_CERT="CAfile"
- REQUESTS_CA_BUNDLE="CAfile"
- CURL_CA_BUNDLE="CAfile"
デフォルトのCA証明書
デフォルトのCA証明書はpipの中にあるcertifi
モジュールの中にある。下記の図のようにsite-packagesの直下ではなくpipディレクトリの_vendorディレクトリに組み込んでいる。
環境変数REQUESTS_CA_BUNDLEにca-certificates
で作成されたCAfileパスを設定するのが一番簡単である。
Python urllib.requestの設定
環境変数による設定
- http_proxy=protocol://[user:passwd@]host:port
- https_proxy=protocol://[user:passwd@]host:port
デフォルトのCA証明書
https通信ではsslライブラリ(Openssl)を利用している
import ssl
ssl.get_default_verify_paths()
# Ubuntu22.04
# DefaultVerifyPaths(cafile=None, capath='/usr/lib/ssl/certs', openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='/usr/lib/ssl/cert.pem', openssl_capath_env='SSL_CERT_DIR', openssl_capath='/usr/lib/ssl/certs')
# CentOS Stream9
# DefaultVerifyPaths(cafile='/etc/pki/tls/cert.pem', capath='/etc/pki/tls/certs', openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='/etc/pki/tls/cert.pem', openssl_capath_env='SSL_CERT_DIR', openssl_capath='/etc/pki/tls/certs')
Ubuntu22.04の場合
/usr/lib/ssl/certs -> /etc/ssl/certsのシンボリックリンク
/usr/lib/ssl/cert.pem -> 存在しない
CentOS Steam9の場合
/etc/pki/tls/cert.pem -> /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pemのシンボリックリンク
/etc/pki/tls/certs/ -> 存在する。(ca-bundle.crt,ca-bundle.trust.crt)
いずれもディストリビューションが設定したopensslを利用しているのでca-certificates
の仕組みで組み込めばよい。
※Anacondaの場合はAnacondaのopensslの設定を利用しているので異なる。
Python requestsの設定
スクリプトで直接記入
import requests
proxies = {
'http': 'protocol://[user:passwd@]host:port',
'https': 'protocol://[user:passwd@]host:port',
}
verify = '/path/to/certfile'
requests.get('https://github.com', proxies=proxies, verify=verify)
環境変数
- http_proxy=protocol://[user:passwd@]host:port
- HTTP_PROXY=protocol://[user:passwd@]host:port
- https_proxy=protocol://[user:passwd@]host:portt
- HTTPS_PROXY=protocol://[user:passwd@]host:portt
- no_proxy
- NO_RROXY
- REQUESTS_CA_BUNDLE="CAfile"
- CURL_CA_BUNDLE="CAfile"
デフォルトのCA証明書
CAバンドルはcertifi
ライブラリを使用
import certifi
certifi.where()
# .venv/lib/python3.11/site-packages/certifi/cacert.pem
このcacert.pemにPEM形式のファイルを追記等を行うか、環境変数REQUESTS_CA_BUNDLEにca-certificates
で作成されたCAfileパスを設定するのが一番簡単である。
wgetの設定
コマンドオプションによる設定
wget --proxy-user=<user> \
--proxy-password=<password> \
--ca-certificate=<file> \
--ca-directory=<directory>
※ --http-proxyのようなオプションはないので、環境変数とかでカバーする。
設定ファイル
http_proxy = <http_proxy>
https_proxy = <https_proxy>
no_proxy = <no_proxy>
proxy_user = <user>
proxy_password = <password>
ca_certificate = <file>
ca_directory = <directory>
環境変数
- http_proxy=protocol://[user:passwd@]host:port
- https_proxy=protocol://[user:passwd@]host:port
- no_proxy
デフォルトのCA証明書
ディストリビューションのOpenSSLからで、
CAfile: "OpenSSL directory"配下のcert.pem
CApath: "OpenSSL directory"配下のcertsディレクトリ
となる。
"OpenSSL directory"はopenssl version -d
で確認可能。
$ openssl version -d
# Ubuntu 22.04
# OPENSSLDIR: "/usr/lib/ssl"
# CentOS Stream 9
# OPENSSLDIR: "/etc/pki/tls"
Ubuntu22.04の場合
/usr/lib/ssl/certs -> /etc/ssl/certsのシンボリックリンク
/usr/lib/ssl/cert.pem -> 存在しない
CentOS Steam9の場合
/etc/pki/tls/cert.pem -> /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pemのシンボリックリンク
/etc/pki/tls/certs/ -> 存在する(ca-bundle.crt,ca-bundle.trust.crt)
基本的にca-certificates
の仕組みで組み込めばよい。
gitの設定
設定ファイル
git config
コマンドで~/.gitconfig
に書き込む
git config --global user.name "<user>"
git config --global user.email "<e-mail>"
git config --global http.proxy "<[protocol://][user[:password]@]proxyhost[:port]>"
git config --global http.sslCAInfo "<cafile>"
git config --global http.sslCAPath "<capath>"
※https.proxyという設定はない!
確認
git config --global --list
~/.gitconfig
(ini形式に近いが同じではない。)
ちなみにWindowsではC:\Users\(ユーザー)\.gitconfig
[user]
name = <user>
email = <e-mail>
[http]
proxy = <[protocol://][user[:password]@]proxyhost[:port]>
sslCAInfo = <cafile>
sslCAPath = <capath>
環境変数
curlの環境変数に加えて
- http_proxy=protocol://[user:passwd@]host:port
- HTTPS_PROXY=protocol://[user:passwd@]host:port
- https_proxy=protocol://[user:passwd@]host:port
- ALL_PROXY
- NO_PROXY
- no_proxy
- CURL_CA_BUNDLE: CA証明書のパス
- GIT_SSL_CAINFO
- GIT_SSL_CAPATH
デフォルトのCA証明書
curlと同じなので基本はca-certificates
の仕組みで組み込めばよい。
Condaの設定
設定ファイル
YAML形式で書かれる
Linux: ~/.condarc
Windows: C:\Users\(ユーザー)\Anaconda3\.condarc
conda config
コマンドによる入力
conda config --set proxy_servers.http <protocol://[user:passwd@]host:port>
conda config --set proxy_servers.https <protocol://[user:passwd@]host:port>
conda config --set ssl_verify <file.pem>
設定を見る
conda config --show
直接入力
proxy_servers:
http: <protocol://[user:passwd@]host:port>
https: <protocol://[user:passwd@]host:port>
ssl_verify: <file.pem>
環境変数
- HTTP_PROXY=protocol://[user:passwd@]host:port
- HTTPS_PROXY=protocol://[user:passwd@]host:port
- REQUESTS_CA_BUNDLE
- CURL_CA_BUNDLE
デフォルトのCA証明書
condaの中にあるopensslを利用していると思われる。
$ ~/miniconda3/bin/openssl version -d
# OPENSSLDIR: "~/miniconda3/ssl"
これを見ると~/miniconda3/ssl/cert.pem
-> ~/miniconda3/ssl/cacert.pem
がCA証明書となる。
環境変数REQUESTS_CA_BUNDLEにca-certificates
で作成されたCAfileパスを設定するのが一番簡単である。
まとめ
どのプログラムでも可能な一番簡単な設定は
-
ca-certificates
でCA証明書を取り込む - 環境変数ですべて設定する
設定例
export proxy_host=proxy.server
export proxy_port=8080
# どちらかを利用、ただしCIDR表記は認識しないプログラムが多いと思われる。ドメインはワイルドカードなしで記入する。
#no_use_proxy=192.168.0.0/16
no_use_proxy="$(echo 192.168.1.{1..255})"
no_use_proxy="${no_use_proxy// /,}"
no_use_proxy="$no_use_proxy",.example.com
export no_use_proxy
# Usage: set_proxy
function set_proxy() {
read -p "Please enter your name: " __user
read -sp "Please enter your password: " __pass ; echo
# パーセントエンコーディング
user=$(echo "$__user" | jq -Rr @uri)
pass=$(echo "$__pass" | jq -Rr @uri)
proxy="http://${user}:${pass}@${proxy_host}:${proxy_port}"
http_proxy="$proxy"
HTTP_PROXY="$proxy"
https_proxy="$proxy"
HTTPS_PROXY="$proxy"
no_proxy=127.0.0.1,localhost,"$no_use_proxy"
NO_PROXY="$no_proxy"
osName=$(grep ^ID= /etc/os-release | sed s/ID=// | sed s/\"//g)
if [[ "$osName" == "ubuntu" ]]; then
CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
fi
if [[ "$osName" == "centos" ]]; then
CA_BUNDLE=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
fi
REQUESTS_CA_BUNDLE="$CA_BUNDLE"
CURL_CA_BUNDLE="$CA_BUNDLE"
export http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY
export REQUESTS_CA_BUNDLE CURL_CA_BUNDLE
}
# Usage: unset_proxy
function unset_proxy() {
unset http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY
unset REQUESTS_CA_BUNDLE CURL_CA_BUNDLE
}
※jqを使用しています。
セキュリティの観点からユーザー名とパスワードを入力するようにした。
使用するときにset_proxyと入力してユーザー名とパスワードを入力する。
削除する際はunset_proxyと入力する。