CentOS7 firewall の設定を一括でしたい

CentOS7になってから結構経ちましたが
Firewallの設定が未だにめんどくさいと思っていたので
思い切って必要最低限勝手にできるやつ作りました。

という個人的メモ的な

# やりたいこと
・publicゾーンは、サービスとポートで制限をかける
・ssh用のゾーンを１つ作成し、SSHに対してIP制限をかける
・できれば、長ったらしいコマンドは書かないようにしたい
・こうしたいって思ったら修正変えやすい形がいい

# 必要そうなコマンドを流れに合わせて書いてみる
### 流れ
firewallが動いているかを確認する
↓
現在のアクティブのゾーンは何かを確認する
↓
アクティブのゾーンに追加したいサービスを追加する
↓
アクティブのゾーンに追加したいポートを追加する
↓
SSH用のゾーンを作成する
↓
SSH用のゾーンをACCEPTのゾーンとして登録する
↓
SSH用のゾーンにSSHサービスを追加する
↓
SSH用のゾーンに接続を許可するIPを追加する
↓
アクティブのゾーンからSSHを削除する
↓
設定をリロードする
↓
（必要ないけど）サービスを再起動する
### コマンド

<details><summary>長いのでおりこみ</summary><div>

そもそも動いているかを確認する

```
]# systemctl status firewalld
```

※動いていなかった場合

```
]# systemctl start firewalld
```


現在のアクティブのゾーンを確認する

```
]# firewall-cmd --get-active-zone
public
 interfaces: enp0s3
```

出てきたゾーン名（public）が現在firewallでアクティブになっているゾーン

アクティブのゾーンに設定されている状態を確認する

```
]# firewall-cmd --list-all
public (active)
   target: default
   icmp-block....
```

ここに出てくるのが、現在アクティブのゾーンに設定されている内容

publicゾーンにhttpサービスを追加する

```
]# firewall-cmd --add-service=HTTP --zone=public --permanent
```

--add-service : 追加を行うサービス名
--zone= : サービスを追加するゾーン、このオプションがない場合デフォルトのゾーンが指定される（大体は追加するゾーンは決まっているので、書いてあったほうがいい）
--permanent : このオプションが付くと、firewallが再起動されても有効な状態になる

publicゾーンに8080/tcpポートを追加する(TCPポート)

```
]# firewall-cmd --add-port=8080/tcp --zone=public --permanent
```

publicゾーンに8080/udpポートを追加する(UDPポート)

```
]# firewall-cmd --add-port=8080/udp --zone=public --permanent
```

この様に、ポート番号／ポートタイプ を「--add-port=」に指定する

SSH用のゾーンを作成する

```
]# firewall-cmd --new-zone=ssh --permanent
```

念のため、追加した時点で一度リロードを行う

```
]# firewall-cmd --reload
```

作成したゾーンをACCEPTとして登録する

```
]# firewall-cmd --zone=ssh --set-target=ACCEPT --permanent
```

--set-target : SSHゾーンの登録をどのような形でするのかを設定する
>ACCEPT : 接続の許可
>REJECT : 接続の拒否

sshゾーンにサービスとしてSSHを登録する

```
]# firewall-cmd --add-service=ssh --zone=ssh --permanent
```

SSH接続を許可するIPを登録する

```
]# firewall-cmd --add-source=192.168.1.123/32 --zone=ssh --permanent
```

--add-source : 今回の場合は、接続を許可するIPアドレス
　　　　　　　　　192.168.1.123/32 という形で登録する

登録が終わったら再度リロードを行う

```
]# firewall-cmd --reload
```

publicゾーンからSSHを削除する

```
]# firewall-cmd remove-service=ssh --zone=public --permanent
```

SSHのゾーンを削除する前に行っても大丈夫ですが、ミスったときにSSH接続ができなくなるのである程度問題なくなってから削除するのをおすすめします

リロードをしてから、サービスを再起動する

```
]# firewall-cmd --reload
]# systemctl restart firewalld
```

</div></details>

# 長い
iptableのときよりやることが増えた感がすごい
あるものを使いたい精神なので、そう行ってもこれから離れられないんです…

# そこでシェルスクリプト
書いたようなコマンドを全て内包して、ある程度自動でやってくれるように書いてしまおう！

あれもこれもと欲張った結果がこちらです

```sh:firewall_settngs.sh
# !/bin/sh
## ==============================================================
## firewall setting
## ==============================================================
LINE="============================================================"
O_LINE="------------------------------------------------------------"

# public zone setting
PUBLIC_SERVICE=("ssh false" "http true" "https true" "dhcpv6-client true")
PUBLIC_PORT=("8080/tcp true" "8080/ucp true")
PUBLIC_SOURCE=("")

# ssh zone setting
SSH_SERIVECE=("ssh true")
SSH_PORT=("")
SSH_SOURCE=("127.0.0.1/32 true" "192.168.59.111/32 true")

# * when adding a new zone
#


## ==============================================================
## result
## ==============================================================
result_success () {
  echo -e "${1} : [\e[34m success \e[m]"
}
# result_success "aaaa"
result_warning () {
  echo -e "${1} : [\e[31m failure \e[m]";
}
result_q () {
  if [ "${2}" = "success" ]; then
    result_success "${1}"
  elif [ "${2}" = "failure" ]; then
    result_warning "${1}"
  else
    echo -e "${1} : [\e[35m ${2} \e[m]"
  fi
}
cmd () {
  result=`${2}`
  result_q "${1}" "${result}"
}


## ==============================================================
## firewall cmd
## ==============================================================
firewall_service () {
    service=`firewall-cmd --list-service --zone=${1} | grep "${2} "`
    if [ "${?}" -eq 0 ]; then
        result1=`firewall-cmd --remove-service=${2} --zone=${1} --permanent`
        result_q "delete service ${2}" ${result1}
    fi

    if [ "${3}" = "true" ]; then
        result2=`firewall-cmd --add-service=${2} --zone=${1} --permanent`
        result_q "add service ${2}" ${result2}
    fi
}

firewall_port () {
    port=`firewall-cmd --list-port --zone=${1} | grep "${2} "`
    if [ "${?}" -eq 0 ]; then
        result1=`firewall-cmd --remove-port=${2} --zone=${1} --permanent`
        result_q "delete port ${2}" ${result1}
    fi

    if [ "${3}" = "true" ]; then
        result2=`firewall-cmd --add-port=${2} --zone=${1} --permanent`
        result_q "add port ${2}" ${result2}
    fi
}

firewall_ip () {
    ip=`firewall-cmd --list-sources --zone=${1} | grep "${2} "`
    if [ "${?}" -eq 0 ]; then
        result1=`firewall-cmd --remove-source=${2} --zone=${1} --permanent`
        result_q "remove ip ${2}" ${result1}
    fi

    if [ "${3}" = "true" ]; then
        result2=`firewall-cmd --add-source=${2} --zone=${1} --permanent`
        result_q "add ip ${2}" ${result2}
    fi
}

zone_accept () {
  echo ""
  echo "${O_LINE}"
  echo "${1} settings"
  echo "${O_LINE}"
  cmd "delete zone ${1}" "firewall-cmd --permanent --delete-zone=${1}"
  cmd "new zone ${1}" "firewall-cmd --permanent --new-zone=${1}"
  cmd "firewall reload" "firewall-cmd --reload"
  cmd "ACCEPT zone" "firewall-cmd --permanent --zone=${1} --set-target=ACCEPT"
}

firewall_zone () {
    # firewall public zone is default active zone
    echo "${O_LINE}"
    echo "public settings"
    echo "${O_LINE}"
    for (( I = 0; I < ${#PUBLIC_SERVICE[@]}; ++I )); do
        set -- ${PUBLIC_SERVICE[I]}
        firewall_service "public" ${1} ${2}
    done
    for (( I = 0; I < ${#PUBLIC_PORT[@]}; ++I )); do
        set -- ${PUBLIC_PORT[I]}
        firewall_port "public" ${1} ${2}
    done
    for (( I = 0; I < ${#PUBLIC_SOURCE[@]}; ++I )); do
        set -- ${PUBLIC_SOURCE[I]}
        firewall_ip "public" ${1} ${2}
    done

    # firewall ssh zone is add active zone
    ssh="ssh"
    zone_accept "${ssh}"
    for (( I = 0; I < ${#SSH_SERVICE[@]}; ++I )); do
        set -- ${SSH_SERVICE[I]}
        firewall_service "${ssh}" ${1} ${2}
    done
    for (( I = 0; I < ${#SSH_PORT[@]}; ++I )); do
        set -- ${SSH_PORT[I]}
        firewall_port "${ssh}" ${1} ${2}
    done
    for (( I = 0; I < ${#SSH_SOURCE[@]}; ++I )); do
        set -- ${SSH_SOURCE[I]}
        firewall_ip "${ssh}" ${1} ${2}
    done

    # new zone status added below


    # end a "zone" setting
    cmd "firewall reload" "firewall-cmd --reload"
}

firewall_status () {
  C_S="\e[37;41;1m"
  C_E="\e[m"
  cat << EOF
${O_LINE}
ZONE:${1}
service:
  `firewall-cmd --list-service --zone=${1}`
port:
  `firewall-cmd --list-port --zone=${1}`
sources:
  `firewall-cmd --list-sources --zone=${1}`
${O_LINE}
EOF
}

view_status () {
  echo ""
  echo "${LINE}"
  echo "setting firewall view"
  echo "${LINE}"
  firewall_status "public"
  firewall_status "ssh"
  # new zone status added below

}


## ==============================================================
## firewalld check
##
## return code
##   -1  : running iptables
## ==============================================================
firewalld_check () {
  echo "${LINE}"
  echo "firewall running check"
  echo "${LINE}"

  r01=`systemctl status firewalld | grep running`
  if [ -n "${r01}" ]; then
    result_success "firewalld"

    return 0
  else
    result_warning "firewalld"
    r02=`systemctl status iptables | grep running`
    if [ -n "${r02}" ]; then
      result_success "iptables"
      echo "# is running iptables system"

      return 1
    fi

    result_warning "iptables"
    echo "\e[31m*** No Resistance Tactice  ***\e[m"
  fi

  return 2
}


## ==============================================================
## update command
## ==============================================================
update_line () {
  r01=`firewalld_check`
  if [ "${?}" -eq 1]; then
    printf "${r01}\n"
    exit 1
  elif [ "${?}" -eq 2]; then
    printf "${r01}\n"
    echo "${O_LINE}"
    echo "start firewalld"
    echo "${O_LINE}"
    eval "systemctl start firewalld"
    if [ "${?}" -ne 0]; then
      exit 1
    fi
  else
    printf "${r01}\n"
  fi
  firewall_zone
  cmd "restart service" "systemctl restart firewalld"
  view_status
}


## ==============================================================
## check command
## ==============================================================
check_line () {
  r01=`firewalld_check`
  if [ "${?}" -eq 0 ]; then
    printf "${r01}\n"
    view_status
  else
    printf "${r01}\n"
  fi
}


## ==============================================================
## help command
## ==============================================================
help_line () {
  cat << EOF

this script was created to
automatically update the Firewalld of CentOS 7.

command.
    [-h]   : script help
    [-c]   : check firewall setting
    [-u]   : update firewall

EOF
}

usage_exit () {
  echo "Usage: ${0} [-h] [-c] [-u]"
  exit 1
}

while getopts hcu option
do
  case $option in
    h)
      help_line
      exit 0 ;;
    c)
      check_line
      exit 0 ;;
    u)
      update_line
      exit 0 ;;
    \?)
      usage_exit ;;
  esac
done
usage_exit
```

public ゾーンに登録したいサービス

```
PUBLIC_SERVICE=("ssh false" "http true" "https true" "dhcpv6-client true")
```

public ゾーンに登録したいポート

```
PUBLIC_PORT=("8080/tcp true" "8080/ucp true")
```

public ゾーンに登録したいIP（publicゾーンでは使用しないが念のため）

```
PUBLIC_SOURCE=("")
```

SSH用のゾーンに登録したいサービス

```
SSH_SERIVECE=("ssh true")
```

SSH用のゾーンに登録したいポート

```
SSH_PORT=("")
```

SSH用のゾーンに登録したいIP

```
SSH_SOURCE=("127.0.0.1/32 true" "192.168.59.111/32 true")
```

全ての入力内容に共通しているものとして、スペース区切りでtrueかfalseか入れるようにしてあります。

trueなら、登録
falseなら、削除はするが登録はしない（いらなくなったものとか）

呼び出し方は

```
]# ./firewall_settngs.sh [-h] [-c] [-u]
```
[-h] : よくあるコマンドのヘルプ
[-c] : 現在設定されている項目の出力（public, ssh）
[-u] : 設定されている内容で、Firewallを更新する


# 終わりに
このシェルスクリプトでは、activeになっているゾーンはpublicという決め打ちになっているので、そうじゃなかった時が修正めんどくさそうだけど、そうなっているのは基本的にいじっている後なので、これ使う必要あるのかなぁと思います。

これでサーバを適当に作ったときのめんどくさい作業が少しでも減れば…