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.

big-ip でSSLクライアント認証&外部のOCSPレスポンダを使って証明書の失効を管理する

AskF5 | Manual Chapter: Configuring Remote SSL OCSP Authentication
参考:hogem.hatenablog.com


URLはpoolではなく、直接指定しかできないから、真面目に使うならサーバ間でHAとかしないとダメ?
と思ったけどvirtualserverのIP宛でも指定できたので、事前に冗長構成のpool, virtualserverを作っておけば良さげ。


事前準備 CA

opensslなどでCA証明書作っておいてbig-ipに取り込んでおく
System - File Management - SSL Certificate List

事前準備 ocsp レスポンダ

ocspレスポンダを起動しておく。今回はopenssl ocspを利用

cd /etc/pki/CA
openssl ocsp -ignore_err -index index.txt -CA cacert.pem -rsigner cacert.pem -rkey private/cakey.pem -port 8888

ちなみにこのopenssl ocsp レスポンダ、別ターミナルから証明書失効させて index.txtを更新したとしても
反映はされないので、必要に応じて再起動させる必要がある。マイブームのinotify使えば良い気はする。
ただ、間違った形式のindex.txtにしてしまって自動再起動したら認証できなくなるので、
やはり、都度手動で再起動するべきかな。


試験用の別portで起動してみて、そこで認証がOKだったら本番OCSPレスポンダも再起動
というところまで作り込めば自動再起動でも良い。

ssl profile

Local Traffic - Virtual Servers - Profiles

  • Configuration

Certificate, Key, 必要ならChainを選ぶ
(ここまでは通常のSSLと同じ)

  • Client Authentication

Client Certificate: require
Frequency: once
# 要件次第
# alwaysだとアクセスごとにOCSPレスポンダに問い合わせが行くのでセキュリティレベルは上がるけど、サーバ負荷になる
# onceだと一度アクセス時したあとはブラウザがセッション閉じるまでは再問い合わせしない
Trusted Certificate Authorities: importしたcaを選択
Advertised Certificate Authorities: None # 詳細不明、↑と同じでも良い?
Certificate Revocation List (CRL): 今回はOCSP使うのでnoneで。

OCSPレスポンダ

3つの設定が連動してる

Local Traffic - Virtual Servers - Profiles - Authentication - OCSP Responders
URL: 起動済みのocsp レスポンダのURL # http://192.168.0.1:8888/
Certificate Authority File: ssl profileのTrusted Certificate Authoritiesを同じca

  • OCSP Configuration

Local Traffic - Virtual Servers - Profiles - Authentication - Configuration
Type: SSL OCSP
Responders: 上で作ったocsp responders

Local Traffic - Virtual Servers - Profiles - Authentication - Profiles
Type: SSL OCSP
Parent Profile: ssl_ocsp
Configuration: 上で作ったconfiguration

Virtual Server

  • General Properties

ip, port 等は設定したい値で

  • Configuration

SSL Profile(client): 上で作ったssl profile
Authentication Profiles: 上で作ったocsp profile

snmpでlinuxのdisk パーティション監視

cacti/cloudforecastってどやって取ってるん?とふと気になって調べた。
cloudforecastのlib/CloudForecast/Data/Disk.pm 見てたらhrStorage* という単語あったので
snmpのoid見てたらこの辺だった。

各サーバのsnmpd.conf にdiskの設定追加しなくても良い方法 (cacti、cloudforecastが使ってるのはこのoid)

使ってるのはこれ以下のoidツリー

# snmptranslate .1.3.6.1.2.1.25.2
HOST-RESOURCES-MIB::hrStorage

まず HOST-RESOURCES-MIB::hrStorageIndex でパーティションに対応するindex調べる

# snmpwalk -v2c -c public localhost HOST-RESOURCES-MIB::hrStorageDescr
(略)
HOST-RESOURCES-MIB::hrStorageDescr.31 = STRING: /

=> /は31

そのindexのsizeと単位調べる

# snmpwalk -v2c -c public localhost HOST-RESOURCES-MIB::hrStorageAllocationUnits.31
HOST-RESOURCES-MIB::hrStorageAllocationUnits.31 = INTEGER: 4096 Bytes
# snmpwalk -v2c -c public localhost HOST-RESOURCES-MIB::hrStorageSize.31
HOST-RESOURCES-MIB::hrStorageSize.31 = INTEGER: 7065055

=> パーティション容量は4096*7065055/1024/1024/1024.0 = 約27GB

# snmpwalk -v2c -c public localhost HOST-RESOURCES-MIB::hrStorageUsed.31
HOST-RESOURCES-MIB::hrStorageUsed.31 = INTEGER: 781729

=> 使用量は4096*781729/1024/1024/1024.0 = 約3GB

使用率は直接mibでは取れないのでhrStorageUsed / hrStorageSize で計算する

snmpd.confに追加する方法

disk / 10000
disk /boot 10000

これでUCD-SNMP-MIB::dskTable (1.3.6.1.4.1.2021.9) 以下のoidツリーが使えるようになる

  • mount path

1.3.6.1.4.1.2021.9.1.2
UCD-SNMP-MIB::dskPath

  • 使用率

1.3.6.1.4.1.2021.9.1.9
UCD-SNMP-MIB::dskPercent