LoginSignup
1
3

More than 3 years have passed since last update.

RHEL 8 での HA Proxy のインストール

Last updated at Posted at 2020-10-08

1.環境

RHEL 8.2 を使用しました。

# cat /etc/redhat-release 
Red Hat Enterprise Linux release 8.2 (Ootpa)
#

2.インストール

yum -y  install haproxy

もしくは

dnf -y  install haproxy

これだけです。

3.作業に必要な基本コマンド

HA Proxyは、systemctlで操作できるので、基本操作は大体想像通りです。
作業時のコピペ用に、良く使うコマンドをリストしておきます。

# 起動  
$ systemctl start haproxy

# 停止
$ systemctl stop haproxy

# 設定のリロード
$ systemctl reload haproxy

# ステータスの確認
$ systemctl status haproxy

#自動起動
$ systemctl enable haproxy

#自動起動設定確認
$ systemctl is-enabled haproxy

# haproxy.cfg の書式確認 (書いた設定ファイルにエラーが無いか確認するコマンド)
$ haproxy -f /etc/haproxy/haproxy.cfg -c

# ログの tail 
tail -f tail -f /var/log/haproxy.log

4.設定

この手順では、以下のような構成を考えます。
image.png
haproxy.example.localdomain が受けた HTTP/HTTPS リクエストを背後の2つの nginx サーバーにラウンドロビンで渡す構成です。

リクエストをForwardする先のnginxのインストール・構成については、この手順では触れません。

4.1.haproxy.cfgの設定

設定として編集するのは、/etdc/haproxy/haproxy.cfg のみです。

デフォルト(サンプル)の設定で有効になっている 5001~5004 のポートなどは、RHEL 上では SELinuxHAProxy にアクセスを許可しておらずエラーになるので関連ヶ所はコメントアウトが必要です。

/etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# Example configuration for a possible web application.  See the
# full configuration options online.
#
#   https://www.haproxy.org/download/1.8/doc/configuration.txt
#
#---------------------------------------------------------------------

#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
    # to have these messages end up in /var/log/haproxy.log you will
    # need to:
    #
    # 1) configure syslog to accept network log events.  This is done
    #    by adding the '-r' option to the SYSLOGD_OPTIONS in
    #    /etc/sysconfig/syslog
    #
    # 2) configure local2 events to go to the /var/log/haproxy.log
    #   file. A line like the following can be added to
    #   /etc/sysconfig/syslog
    #
    #    localdomain2.*                       /var/log/haproxy.log
    #
    log         127.0.0.1 local2

    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon

    # turn on stats unix socket
    stats socket /var/lib/haproxy/stats

    # utilize system-wide crypto-policies
    ssl-default-bind-ciphers PROFILE=SYSTEM
    ssl-default-server-ciphers PROFILE=SYSTEM

#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
 defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000

#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
# 以下はコメントアウト
# frontend main
#    bind *:5000
#    acl url_static       path_beg       -i /static /images /javascript /stylesheets
#    acl url_static       path_end       -i .jpg .gif .png .css .js

#    use_backend static          if url_static
#    default_backend             app

 frontend http_80
    default_backend http_80
    mode http
    bind *:80

 frontend http_443
    default_backend http_443
    mode http
    bind *:443

#---------------------------------------------------------------------
# static backend for serving up images, stylesheets and such
#---------------------------------------------------------------------
# backend static
#    balance     roundrobin
#    server      static 127.0.0.1:4331 check

#---------------------------------------------------------------------
# round robin balancing between the various backends
#---------------------------------------------------------------------
# 以下はコメントアウト (SELinux に怒られる)
#  backend app
#    balance     roundrobin
#    server  app1 127.0.0.1:5001 check
#    server  app2 127.0.0.1:5002 check
#    server  app3 127.0.0.1:5003 check
#    server  app4 127.0.0.1:5004 check

backend http_80
    mode http
    balance   roundrobin
    server nginx1 nginx1.example.localdomain:80 check
    server nginx2 nginx2.example.localdomain:80 check

backend http_443
    mode http
    balance   roundrobin
    server nginx1 nginx1.example.localdomain:443 check
    server nginx2 nginx2.example.localdomain:443 check

上記の config 模式図化したのが以下の図になります。
image.png

4.2.書式が合っているか確認

設定ファイル(/etc/haproxy/haproxy.cfg) が長いので以下のコマンドで構文間違いを確認できます。

# haproxy.cfg の書式の確認(エラーがあった場合)
$ haproxy -f /etc/haproxy/haproxy.cfg -c
[WARNING] 226/115610 (34205) : parsing [/etc/haproxy/haproxy.cfg:124] : backend 'worker_http', another server named 'worker1' was defined without an explicit ID at line 123, this is not recommended.
[WARNING] 226/115610 (34205) : parsing [/etc/haproxy/haproxy.cfg:129] : backend 'worker_https', another server named 'worker1' was defined without an explicit ID at line 128, this is not recommended.
Configuration file is valid
$

#確認が上手く行ったケース
$ haproxy -f /etc/haproxy/haproxy.cfg -c
Configuration file is valid
$ 

問題が無い場合は、Configuration file is validと表示されるはずです。

5.HTTP/HTTPS用の Firewall の設定

RHELでは標準で firewalldが有効になっているので、ロードバランシングするサービスに応じて穴開けをしてあげる必要があります。

5.1.現在の設定の確認

現在の設定を確認します。

$ firewall-cmd --get-active-zones
libvirt
  interfaces: virbr0
public
  interfaces: ens192
$

interfaces: ens192は、public ゾーンに存在しています。
とりあえず、設定が必要なのは「public」のゾーンであると記憶します。
次は「public」に設定されている「サービス」を確認します。

$ firewall-cmd --list-services --zone=public
cockpit dhcpv6-client ssh
$

5.2.http / https の穴を開ける

http (80)https (443)については、デフォルトで「サービス」の事前定義が存在しているので、それを追加するだけで firewalldに穴を開ける事ができます。

$ firewall-cmd --add-service=https --zone=public  --permanent
success
$ firewall-cmd --add-service=http --zone=public  --permanent
success

firewalld の設定のリロードします。(--permanentで追加した場合は、reload しないと設定が反映されない)

$ firewall-cmd --reload

設定が反映されたか確認します。

$ firewall-cmd --list-services --zone=public
cockpit dhcpv6-client http https ssh

httphttps が追加されています。

ここでは、その他のデフォルトで穴が開いているサービスは特に変更していませんが、必要に応じてハードニングしましょう。

不必要な「サービス」は以下で削除できます。

firewall-cmd --remove-service=<サービス名> --zone=public --permanent

6.Haproxy の起動と動作の確認

6.1 Haproxyの起動

Haproxy を起動させます。

#  haproxy を起動
$ systemctl start haproxy

自動起動を有効化します。

$ systemctl enable haproxy
$ systemctl is-enabled haproxy
enabled       

6.2 動作の確認

http(s)://haproxy.example.localdomain にアクセスしてみます。

この例では、トラフィックを割り振るバックエンドの nginxの画面をわかりやすいように変更していますが、リロードする度に接続先が切り替わるはずです。これは、haproxy.cfgroundrobinを設定しているせいです。

image.png

7.Security の設定をとりあえず回避して HA Proxy の動きだけ見たい場合

HA Proxyの設定は、複雑になってくると、まずはRHELのセキュリティ設定をOFFにしてhaproxy.cfgの設定を確認したい時がどうしても出てきます。

以下は、RHELの標準のセキュリティ設定(SELinuxFirewalld) を停止させる方法です。

7.1.SELinux を停止

SELinux を一時的に停止する

$ setenforce 0   # 反対に稼働させるには 1 を指定

SELinux を恒久的に停止する

$ vim /etc/selinuxconig
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
SELINUX=enforcing  # ここを disabled に
・・・
・・
・

7.2.Firewalld の停止

今動いているものを一時的に止める。

$ systemctl stop firewalld

システム起動時にも上がってこないようにする

$ systemctl disable firewalld

8.一般的なfirewalldの穴あけ方法

HA Proxy は L4 (TCP)レベルのロードバランサーなので、HTTP/HTTPS 以外のプロトコルのロードバランスも行う事ができます。

8.1.事前定義された「サービス」の穴開けを行う

前述の通り http (80)https (443)については、デフォルトで「サービス」の定義が存在しているので、その定義を使用して比較的簡単に firewalld に穴を開ける事ができます。

デフォルトで事前定義されている「サービス」は、firewall-cmd --get-servicesで確認できます。

$ firewall-cmd  --get-services
RH-Satellite-6 amanda-client amanda-k5-client amqp amqps apcupsd audit bacula bacula-client bb bgp bitcoin bitcoin-rpc bitcoin-testnet bitcoin-testnet-rpc bittorrent-lsd ceph ceph-mon cfengine cockpit condor-collector ctdb dhcp dhcpv6 dhcpv6-client distcc dns dns-over-tls docker-registry docker-swarm dropbox-lansync elasticsearch etcd-client etcd-server finger freeipa-4 freeipa-ldap freeipa-ldaps freeipa-replication freeipa-trust ftp ganglia-client ganglia-master git grafana gre high-availability http https imap imaps ipp ipp-client ipsec irc ircs iscsi-target isns jenkins kadmin kdeconnect kerberos kibana klogin kpasswd kprop kshell kube-apiserver ldap ldaps libvirt libvirt-tls lightning-network llmnr machine-config managesieve matrix mdns memcache minidlna mongodb mosh mountd mqtt mqtt-tls ms-wbt mssql murmur mysql nfs nfs3 nmea-0183 nrpe ntp nut openvpn ovirt-imageio ovirt-storageconsole ovirt-vmconsole plex pmcd pmproxy pmwebapi pmwebapis pop3 pop3s postgresql privoxy prometheus proxy-dhcp ptp pulseaudio puppetmaster quassel radius rdp redis redis-sentinel rpc-bind rsh rsyncd rtsp salt-master samba samba-client samba-dc sane sip sips slp smtp smtp-submission smtps snmp snmptrap spideroak-lansync spotify-sync squid ssdp ssh steam-streaming svdrp svn syncthing syncthing-gui synergy syslog syslog-tls telnet tentacle tftp tftp-client tile38 tinc tor-socks transmission-client upnp-client vdsm vnc-server wbem-http wbem-https wsman wsmans xdmcp xmpp-bosh xmpp-client xmpp-local xmpp-server zabbix-agent zabbix-server
$

ただこれらの名前を見ても一体どういう定義なのかわかりません。

これらのデフォルトで事前定義されている「サービス」の実際の設定ファイルは、/usr/lib/firewalld/services/の下に保管されています。

$ ls  /usr/lib/firewalld/services/
RH-Satellite-6.xml       freeipa-4.xml            libvirt-tls.xml           pop3.xml               ssh.xml
amanda-client.xml        freeipa-ldap.xml         libvirt.xml               pop3s.xml              steam-streaming.xml
amanda-k5-client.xml     freeipa-ldaps.xml        lightning-network.xml     postgresql.xml         svdrp.xml
amqp.xml                 freeipa-replication.xml  llmnr.xml                 privoxy.xml            svn.xml
amqps.xml                freeipa-trust.xml        managesieve.xml           prometheus.xml         syncthing-gui.xml
apcupsd.xml              ftp.xml                  matrix.xml                proxy-dhcp.xml         syncthing.xml
<省略>

例えばkube-apiserver という名前の事前定義の「サービス」があります。
このサービスは、「サービス」名と同じフィル名の/usr/lib/firewalld/services/kube-apiserver.xmlというファイルの中で定義されています。中身は読むとなんとなく判別できるものになっています。

$ cat /usr/lib/firewalld/services/kube-apiserver.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>Kubernetes Api Server</short>
  <description>The Kubernetes API server validates and configures data for the api objects which include pods, services, replicationcontrollers, and others.</description>
  <port protocol="tcp" port="6443"/>
</service>
$

「サービス」の定義ファイルの中身は上記のようなものなので、自分が使いたい「サービス」が事前定義されているかは、ファイルの中身を検索する事で発見できます。例えばポート6443を使うサービスが事前定義されているかどうかは、以下のコマンドで検索できます。

$ find /usr/lib/firewalld/services -type f -print | xargs grep 6443
/usr/lib/firewalld/services/kube-apiserver.xml:  <port protocol="tcp" port="6443"/>
$

事前定義されている「サービス」の firewalld への穴開けの方法は、基本的に全て同じで、例えばkube-apiserverpublicゾーンに追加するには以下のようにコマンドを実行します。

# permanent (再起動後も有効)に、設定を追加
$ firewall-cmd --add-service=kube-apiserver --zone=public  --permanent
success

# 設定の再読込
$ firewall-cmd --reload

8.2.カスタムの「サービス」を作成して穴開けを行う

例として「Machine Config」というサービスがあり、22623 /tcp というポートを使用したいとします。
まずは、以下のコマンドで自分の使用しいたいportがデフォルトで事前定義されてないか探してみます。

 find /usr/lib/firewalld/services -type f -print | xargs grep <自分の使用したいサービスのポート>

もし存在しない場合は、自分で新しい「サービス」を定義します。

ここでは、「machine-config」という22623 /tcp を使う新しいサービスを作成してみます。

新しいサービス名の追加

$ firewall-cmd --permanent --new-service machine-config
success

「サービス」「machine-config」の説明を追加

$ firewall-cmd --permanent --service=machine-config --set-description="OpenShift machine config access"
success

新しい「サービス」「machine-config」のポート定義を追加

$ firewall-cmd --service=machine-config --add-port=22623/tcp --permanent 
success

新しい「サービス」の設定ファイルが作成された事を確認します。ユーザー定義の「サービス」は、事前定義の「サービス」のファイルのパスとは違い、/etc/firewalld/services/配下に作成されます。

$ cat /etc/firewalld/services/machine-config.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <description>OpenShift machine config access</description>
  <port port="22623" protocol="tcp"/>
</service>

作成された新しい「サービス」のファイルを読み込みます。

$ firewall-cmd --reload

作成した「サービス」を firewalld を透過させる「サービス」として、ここではpublicゾーンに追加します。

$ firewall-cmd --add-service=machine-config --zone=public 
success

現在、firewalldを透過するように設定されている「サービス」を確認します。machine-configが追加されている事を確認します。

$ firewall-cmd --list-services
cockpit dhcpv6-client http https kube-apiserver machine-config ssh
$

再起動しても設定が外れないように、現在の設定を permanent にするコマンドを実行します。

$ firewall-cmd --runtime-to-permanent
success

9.SELinux の設定

特種なポートを使うアプリケーションのロードバランスを行う場合、HA Proxyがそのポートを使用する事が許可されていない場合は、SELinuxHA Proxyの動きをブロックするため、haproxy が起動しません。

例えば設定を変更して、systemctlで再起動しようとすると、以下のように起動できないはずです。

$ systemctl restart haproxy.service
Job for haproxy.service failed because the control process exited with error code.
See "systemctl status haproxy.service" and "journalctl -xe" for details.
$

9.1 audit.log の確認

SELinuxのログは/var/log/audit/audit.log に出力されるので、haproxy が動かない場合は、動きが止められてないか、audit.logを確認します。

しかし audit.log は、そのままでは見にくいので ausearch というコマンドで、フォーマットされた audit.log を見るツールが用意されています。

haproxy 関連の audit.log のエラーは以下のコマンドで確認する事ができます。

[root@lb1 ~]# ausearch -c 'haproxy' --raw
type=AVC msg=audit(1602169554.880:195): avc:  denied  { name_bind } for  pid=2903 comm="haproxy" src=6443 scontext=system_u:system_r:haproxy_t:s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket permissive=0
type=AVC msg=audit(1602169554.880:196): avc:  denied  { name_bind } for  pid=2903 comm="haproxy" src=22623 scontext=system_u:system_r:haproxy_t:s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket permissive=0
[root@lb1 ~]# 

denied という単語が見えるので、何かが拒否されているのがわかります。

9.2 audit2allow の使用

ausearch での出力された denied のログを audit2allow というコマンドにパイプで渡す事で、行うべき SELinuxの設定を提案してくれます。ここでは、my-haproxyというファイル名にします。

$ ausearch -c 'haproxy' --raw | audit2allow -M my-haproxy
******************** IMPORTANT ***********************
To make this policy package active, execute:

semodule -i my-haproxy.pp

$ ls -ltr
-rw-r--r--. 1 root root  308 Oct  8 11:12 my-haproxy.te
-rw-r--r--. 1 root root  969 Oct  8 11:12 my-haproxy.pp
$

.pp.te というファイルができますが、.teは可読なので中身を確認してみます。

$ cat my-haproxy.te 

module my-haproxy 1.0;

require {
        type haproxy_t;
        type unreserved_port_t;
        class tcp_socket name_bind;
}

#============= haproxy_t ==============

#!!!! This avc can be allowed using one of the these booleans:
#     nis_enabled, haproxy_connect_any
allow haproxy_t unreserved_port_t:tcp_socket { name_bind name_connect };
$

allow haproxy_t unreserved_port_t:tcp_socket name_bind; という設定をする事をする事をすすめられています。

この設定を適用するには、以下のコマンドを実行します。

semodule -i my-haproxy.pp

これで haproxy が起動するようになっているはずです。

10.rsyslog を有効化する

HA Proxy がきちんと動いているかどうか、確認したいなと思い、ログはどこか・・・と探した所、rsyslog で外に吐く設定をしてあげる必要があるようです。/var/log/haproxy.log  というファイルにログを吐くようにします。

/etc/rsyslog.conf を以下のように編集します。

/etc/rsyslog.conf
・・・
# Provides UDP syslog reception
# for parameters see http://www.rsyslog.com/doc/imudp.html
#
#module(load="imudp") # needs to be done just once
#input(type="imudp" port="514")
# 以下の2行を追加
$ModLoad imudp
$UDPServerRun 514

<省略>

# *.info;mail.none;authpriv.none;cron.none                /var/log/messages
# 以下に書き換え (local2 が /var/log/message に出力されないようにするため。 /var/log/haproxy.log に出力したい)
*.info;mail.none;authpriv.none;cron.none;local2.none                /var/log/messages

<省略>

# Save boot messages also to boot.log
local7.*                                                /var/log/boot.log

# ここも追加 /var/log/haproxy.log にログを出力する。
# Save HAProxy maessage to haproxy.log
local2.*                                                /var/log/haproxy.log

設定を編集したらrsyslogを再起動します。

systemctl restart rsyslog
1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3