在RHEL 8上安装HA Proxy
保護是我們共同的責任,我們應該努力保護我們的環境。
我使用了RHEL 8.2。
# cat /etc/redhat-release
Red Hat Enterprise Linux release 8.2 (Ootpa)
#
2. 安装
yum -y install haproxy
或者
dnf -y install haproxy
只需要这些。
做作业所需的基本命令。
由于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.设置 => 设定
这个步骤不涉及到安装和配置用于转发请求的nginx。
4.1. Haproxy配置文件的设置
只需编辑/etdc/haproxy/haproxy.cfg的配置。
在RHEL上,默认(示例)设置下启用的端口5001〜5004等,由于SELinux不允许HAProxy访问,会导致错误,所以需要将相关部分注释掉。
#---------------------------------------------------------------------
# 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
请确认格式是否正确。
因为设置文件(/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
$
如果没有问题,应该显示“配置文件有效”。
5. 针对HTTP/HTTPS的防火墙设置
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”选项进行了添加,需要重新加载才能使设置生效。
$ 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.cfg に roundrobinを設定しているせいです。
如果只是想查看HA Proxy的运行情况,可以暂时避免设置7.Security。
HA Proxyの設定は、複雑になってくると、まずはRHELのセキュリティ設定をOFFにしてhaproxy.cfgの設定を確認したい時がどうしても出てきます。
以下は、RHELの標準のセキュリティ設定(SELinux と Firewalld) を停止させる方法です。
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-apiserverをpublicゾーンに追加するには以下のようにコマンドを実行します。
# 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がそのポートを使用する事が許可されていない場合は、SELinuxがHA 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 审计日志的验证
如果haproxy无法运行,请检查是否已停止运行,或检查audit.log日志文件,该日志文件被输出到/var/log/audit/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 ~]#
我可以看到字词”拒绝”,所以可以理解有某件事被否决了。
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了。
启用rsyslog
HA Proxy がきちんと動いているかどうか、確認したいなと思い、ログはどこか・・・と探した所、rsyslog で外に吐く設定をしてあげる必要があるようです。/var/log/haproxy.log というファイルにログを吐くようにします。
/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