big-ip persistence match acrossメモ

virtualserverを跨って同一のnodeに振り分けたいときのpersistenceのoption

cookieでmatch acrossする場合はbig-ipが発行するinsert cookieは使えない。
cookie hashが必要(webアプリケーション側で発行するcookie名を指定)
Cookie Nameは PHPSESSIDなど。

アプリケーションが一意なら問題ないが、/fooはperl、/barはphpのようにわかれていると難しいのでその場合はsource ip persistenceを使うと良さげ。


source ip persistenceの例

前提としてhttp用のpoolとhttps用のpoolで同一のnodeを含んでいること。

同一ipのvirtualserver (http/https)で同nodeに振り分けたい場合

source_addr: match across services

普通はこれでいい

異なるipのvirtualserver 間で同nodeに振り分けたい場合

(これが必要なときあんまりないと思うが。。。)
同一サーバだけどSSLを設定するために別IPでvirtualserverを作った場合とか?

source_addr: match across virtualservers

match across Pools はおそらく通常用途では使わない。

big-ip irule IP::local_addr

使う場所 (event) 次第で結果が変わる。


clientside (CLIENT_ACCEPTED)の場合はdestination addr
serverside (SERVER_CONNECTED)の場合はsource addr

iruleのCLIENT_ACCEPTEDを使っているところでdestination addrを判別したかって、IP::remote_addrを使ってみたけどsource addrが返ってきた。
IP::server_addrを使うと(たぶんCLIENT_ACCEPTEDの時点ではまだサーバに繋がってないから)"no serverside connection established "となってエラーとなった。
で、色々試行錯誤してて、CLIENT_ACCPTEDの時点でdestination addr知りたかったらどないしたらええねん?と行き着いた先が冒頭の結果通り、IP::local_addrを使って解決。

big-ip source nat part2

hogem.hatenablog.com
追記で書いたけど↑は任意のsourceからとあるStandardなVirtualServerへアクセスしたときのsource nat
とはいえ結局はnatなので半分ぐらいかぶってる。


今回は宛先がVirtualServerではなくて、
特定のserverからbig-ipをdefault gatewayとして抜けていって、他のnetworkへアクセスするときの話。

具体的に言うとdmz(or trustでもいいけど)から
internet(untrust)や違うvlan dmz2 などへアクセスする場合

前提

big-ipがルータとなる場合、networkを跨った通信はデフォルトでは許可されない。

NATするか、typeをForwarding としたVirtualServerの作成が必要。

type: Forwarding (IP)
Source: 通信を許可するsource IP anyの場合は0.0.0.0/0
Destination: 通信を許可するdestination IP
Service Port: 0 (* All Ports)
  (特定portのみに制限することも可能だけど、通常forwardingの場合はall)
VLAN and Tunnel Traffic: 通常は特定の通信だけを設定すると思うのでALL Vlansではなく
   Enabled Onにしてsourceで通すvlan を選択
Source Address Translation: 後述


NATする箇所はおおまかに4通り

  • 双方向通信が必要なNATはNAT List
  • 特定のhost(s)だけSNATするときはSNAT List
  • subnetをまとめてSNATするときはForwarding Virtualserverで、
  • subnetをまとめてSNATするけど、特定のhostだけ個別のIPでSNATさせたい場合はiRuleっていう感じ?


おまけ:
VirtualServerの適用はlonguest matchで適用されるので

  • 特定IP/Portの場合は
  • 特定のtcp profileを適用して (ex: timeoutを伸ばす)
  • NATするIPを変える

というようなことも可能
おまけここまで

http://hogem.hatenablog.com/entry/2015/11/07/233000

Address Translation : NAT List

1:1 NAT 双方向

NAT Address: 172.16.0.1
Origin Address: 10.0.0.1
とすると、10.0.0.1 がbig-ipを抜けて外へ通信する際に172.16.0.1になり、
外のサーバから172.16.0.1へアクセスすると10.0.0.1宛にパケットが届く。

172.16.0.1のアドレスでpingが応答したので双方向NATだと思い込んでたけど
pingはSNATを定義したらbig-ipが応答するようになるだけなので、双方向NATではなかった。

NAT Listで1:1 NATする場合は、forwarding virtual serverの設定しなくても
vlanを跨いで通信は可能になる。

あと、NAT Listで定義してもforwarding virtual server でNAT設定があったら、そちらが有効になるぽい。
(なんしか、big-ipはvirtual serverが最優先されると思っていたらいい?)

Address Translation : SNAT List

N:1 NAT 片方向
複数のprivate IP を特定のgloabal IPにNATする場合など。
Forwarding VirtualServerでまとめてNATするのではなくて
特定のaddress(s)を個別にNATするときに使う。

SNAT Listを設定していてもForwarding VirtualServerでSNATが設定されていた場合、
そちらのでSNATが優先される。

Origin IPを指定する時点で不要な気もするが、送信元をvlanを限定したい場合は
VLAN / Tunnel Traffic をEnabled onにしてVLANを選択する

Forwading VirtualServer 単位でNAT

Source Address TranslationでNoneではなく次の2つのどちらかを選ぶ。
通常はauto mapで良いはず。

  • Auto Map: destinationに適したegressなinterfaceのfloating ipが自動的に選ばれる
  • SNAT: あらかじめ設定しておいたSNAT Poolから選択

例えばdmz の任意のserverがuntrustへ抜けていくときに
まとめてとあるグローバルIPでNATさせたい場合に。

SNAT ListよりもこちらのSNATが優先される。

iRule でNAT

http://hogem.hatenablog.com/entry/20150110/1420894082
これと同じ内容。
条件がマッチした場合はVirtualServerのSNATよりもさらに優先される。

個人的には誤操作による削除防止の観点からも、SNAT Poolを作っておいて
irule内でsnatpool snatpool-hogehoge などとするほうが良い気がする。

# list ltm snatpool snatpool1 one-line
ltm snatpool snatpool1 { members { 192.0.2.11 } }
# list ltm snatpool snatpool2 one-line
ltm snatpool snatpool2 { members { 192.0.2.12 } }
	
### forwarding virtual serverに適用するirule
when CLIENT_ACCEPTED {
  if { [IP::addr [IP::client_addr] equals 10.0.0.11/32] } {
    snatpool snatpool1
  }
  elseif { [IP::addr [IP::client_addr] equals 10.0.0.12/32] } {
    snatpool snatpool2
  }
  else {
    snat automap
  }
}

まとめ

big-ipは仕様上、他で適用されている設定は削除できない。
(virtualhost適用済みのpool/irule、iruleに適用しているsnat poolなど)
ただSNAT Listだけはそうじゃないみたいなので、
特定のhostだけSNATをする場合でも、snat poolを作ってそれをiruleで指定するほうが安全?
その分iruleが複雑になっていくのでどっち選ぶかは運用次第。

環境変数SSLEAY_CONFIGでopenssl.cnfのファイルを指定する

opensslでprivate CAを作るとRHELの場合は/etc/pki/CA/ (openssl.cnfは/etc/pki/tls/openssl.cnf)が自動的に指定されるので、rootじゃないと証明書を作成できない。

一般ユーザで場所を指定したい場合はどうしたらいい?と思ってopenssl ca help をみると" -config file - A config file"とあった。
さらに調べていると/etc/pki/tls/misc/CA にSSLEAY_CONFIG という環境変数を発見

$ grep SSLEAY_CONFIG /etc/pki/tls/misc/CA
# 10-Jan-96 eay    Fixed a few more bugs and added the SSLEAY_CONFIG
REQ="$OPENSSL req $SSLEAY_CONFIG"
CA="$OPENSSL ca $SSLEAY_CONFIG"	

ということなのでややこしいけどSSLEAY_CONFIGにはopenssl.cnf のpathを書くのではなく"-config /path/to/openssl.cnf" というオプションも含めて指定すればいけた。

sendmailで特定のホスト宛にSTARTTLSの接続に失敗して(平文ですら)送信できない現象

/var/log/maillog

STARTTLS=client, error: connect failed=-1, SSL_error=1, errno=0, retry=-1
STARTTLS=client: 26136:error:14082174:SSL routines:SSL3_CHECK_CERT_AND_ALGORITHM:dh key too small:s3_clnt.c:3342:
ruleset=tls_server, arg1=SOFTWARE, relay=tls.ng.example.com, reject=403 4.7.0 TLS handshake failed.
抜粋 dsn=4.0.0, stat=Deferred: 403 4.7.0 TLS handshake failed.

redhatの解決法通り、1024bit以上の鍵を作ってsendmail.mcを変更してもダメだった。
わかっているのは特定のホスト宛だけなのでとりあえずaccess

Try_TLS:192.0.2.10             NO
Try_TLS:tls.ng.example.com     NO

と書いて対応。ちなみにSTARTTLSを完全にやめる場合はホストを指定せずに

Try_TLS: NO

クライアント証明書発行(作成・署名)時のパスワードをターミナルから入力する

/etc/pki/tls/misc/CA -newreq だとパスワードを聞かれるので
内部で実行されている openssl reqを直接実行して、標準入力から読み込むようにする。

スクリプトにがっつりパスワードを書いていて無条件に署名するので
本番運用するCAの場合は取り扱い注意。

やっていること
1. openssl genrsa で暗号化されてない鍵を作成
2. openssl reqではcommon nameは6行目なので、ここにusernameなどを指定する。
3. /etc/pki/tls/misc/CA -signにyesを渡して自動的に証明 (内部的にはopenssl caが実行されてる)
4. openssl pkcs12 でクライアント配布用のpkcs#12ファイルを作成。引数に-password pass:"パスワード" を渡してパスワード付きで。

cd /path/to/usercert/
openssl genrsa 2048 > newkey.pem
openssl req -new -key newkey.pem << EOF > newreq.pem
JP
Osaka
Osaka
Foo Bar Co., Ltd.

username@example.com



EOF
yes | /etc/pki/tls/misc/CA -sign
openssl pkcs12 -export -clcerts -inkey newkey.pem -in newcert.pem \
  -certfile /etc/pki/CA/cacert.pem -out username.p12 -password pass:hogehoge

これを発展させてユーザ名を変数に取るようにしたら機械的に生成できそう。

opensslとcurlでクライアント証明書を使ってHTTPS接続する

1年ぶりにやったらど忘れしてる。

クライアント証明書発行

事前にプライベートCA作っておくこと 詳細は省略

/etc/pki/tls/misc/CA -newca
クライアント証明書用 秘密鍵CSR作成
/etc/pki/tls/misc/CA -newreq

上で作ったファイルがあるディレクトリでCAで署名

/etc/pki/tls/misc/CA -sign

配布用にPKCS#12ファイル作成

openssl pkcs12 -export -clcerts -inkey newkey.pem -in newcert.pem \
  -certfile /etc/pki/CA/cacert.pem -out username.p12

証明書失効

openssl ca -revoke /path/to/newcert.pem
openssl ca -gencrl -out /etc/pki/CA/crl/crl.pem
## 確認
openssl crl -in /etc/pki/CA/crl/crl.pem -text

web上ではrevokeとgencrlを同時にやっている例があったが、自分の環境だと、同時に実行するとcrlは確かに出力されるが、
revokeで指定した証明書は失効状態にならなかった。なのでrevokeしたあとにgencrlするようにしている。

おまけ: pkcs12から証明書、鍵を抜き出す

鍵を暗号化せず出力する場合は-nodesをつける。

CA証明書だけ抜きだす

$ openssl pkcs12 -in file.p12 -out ca.pem -cacerts -nokeys

鍵付き証明書を抜き出す

$ openssl pkcs12 -in file.p12 -out cert.pem -clcerts

証明書だけ抜き出す

$ openssl pkcs12 -in file.p12 -out cert.pem -clcerts -nokeys

鍵だけ抜き出す

$ openssl pkcs12 -in file.p12 -out key.pem -nocerts

クライアント証明書を使って接続確認

curl の-Eオプション使うのが簡単

$ curl https://ssl.example.com -E file.p12:password

備考: -Eでpkcsが使えるのは手元の環境だとmaccurlのみだった

linux CentOS6

centosのmanはpemじゃないとダメと書いてたのでp12から証明書だけ抜きだす

  • 暗号化して鍵を出力した場合
$ openssl pkcs12 -in file.p12 -out cert.pem -clcerts

CentOS5の場合は問題なし

## 引数にpassword
$ curl https://ssl.example.com -E ./cert.pem:password
## もしくは対話式で入力
$ curl https://ssl.example.com -E ./cert.pem

CentOS6の場合
手元の環境で確認した場合、暗号化してたらダメだった。。。

$ curl https://ssl.example.com/ -E ./cert.pem:password
curl: (35) NSS: client certificate not found: ./cert.pem
## 証明書と鍵を分離してもダメ。
$ curl https://ssl.example.com/ -E ./cert.pem:password --key key.pem

こういわれる。同じ鍵、証明書を使ってwgetするといけたので鍵、証明書は問題ないはず。

$ wget --certificate=cert.pem --private-key=key.pem https://ssl.example.com/

ここに同じこと書いてた。stackoverflow.com

  • 暗号化せず鍵を出力した場合
$ openssl pkcs12 -in file.p12 -out cert.pem -nodes -clcerts

CentOS5の場合はカレントディレクトリのファイルの場合ファイル名だけで良かったけど、
CentOS6の場合はフルパスもしくは相対パスで指定しないとエラーになった。

## ng
$ curl https://ssl.example.com -E cert.pem
curl: (35) NSS: client certificate not found: cert.pem
## ok
$ curl https://ssl.example.com -E ./cert.pem
$ curl https://ssl.example.com -E /path/to/cert.pem

man curlになんかそれぽいこと書いてる。

If curl is built against the NSS SSL library then this option can tell curl
the nickname of the certificate to use within the NSS database defined by
the environment variable SSL_DIR (or by default /etc/pki/nssdb). If the NSS
PEM PKCS#11 module (libnsspem.so) is available then PEM files may be loaded.
If you want to use a file from the current directory, please precede it with
"./" prefix, in order to avoid confusion with a nickname.