防火墙,其实说白了讲,就是用于实现Linux下访问控制的功能的,它分为硬件的或者软件的防火墙两种。无论是在哪个网络中,防火墙工作的地方一定是在网络的边缘。而我们的任务就是需要去定义到底防火墙如何工作,这就是防火墙的策略,规则,以达到让它对出入网络的IP、数据进行检测。

目前市面上比较常见的有3、4层的防火墙,叫网络层的防火墙,还有7层的防火墙,其实是代理层的网关。

对于TCP/IP的七层模型来讲,我们知道第三层是网络层,三层的防火墙会在这层对源地址和目标地址进行检测。但是对于七层的防火墙,不管你源端口或者目标端口,源地址或者目标地址是什么,都将对你所有的东西进行检查。所以,对于设计原理来讲,七层防火墙更加安全,但是这却带来了效率更低。所以市面上通常的防火墙方案,都是两者结合的。而又由于我们都需要从防火墙所控制的这个口来访问,所以防火墙的工作效率就成了用户能够访问数据多少的一个最重要的控制,配置的不好甚至有可能成为流量的瓶颈
本次介绍iptables防火墙

iptables防火墙说明及工作原理
iptables防火墙包含四个表和五个链,分别为

四个表分别为:

Filter表(真正的防火墙功能)
Nat表(负责数据包的改写)
Mangle表(路由标记)
raw表(禁止数据包的连接跟踪,用于提高性能)

五个链分别为:
INPUT(进入防火墙)
OUTPUT(离开防火墙)
FORWARD(流经防火墙)
PREROUTING(数据包到达防火墙之前进行路由判断)
POSTROUTING(数据包离开防火墙时进行的路由判断)

3.表和链的包含情况说明

Filter表包含: INPUT、OUTPUT、FORWARD;
Nat表包含: OUTPUT、PREROUTING、POSTROUTING;
Mangle表包含: INPUT、OUTPUT、FORWARD、PREROUTING、POSTROUTING;
Raw表包含: OUTPUT、PREROUTING;

4.iptables防火墙的工作原理说明

1、iptables防火墙是一层层过滤的,实际是按照配置规则的顺序从上到下,从前到后进行过滤的。
2、如果匹配上规则,即明确表明是阻止还是通过,此时数据包就不在向下匹配新规则了。
3、如果所有规则中没有明确表示是阻止还是通过这个数据包,也就是没有匹配上规则,向下进行匹配,直到匹配到默认规 则到明确阻止还是通过。
4、防火墙的默认规则是对应链的所有规则执行完才会执行的。
注:防火墙规则执行顺序默认是从前到后,(从上到下)依次执行,遇到匹配的规则就不在继续向下检查,只有遇到不匹配的规则才会继续向下进行匹配

Iptables防火墙使用及规则配置

iptables命令常用参数
很多参数请通过man iptables或iptables --help来查看

-n:以数字的方式显示ip,它会将ip直接显示出来,如果不加-n,则会将ip反向解析成主机名。
-L(大写)列表 例:iptbles -nL 查看iptables规则
--line-number显示规则的序号 iptables -nL --line-number (可以根据序号删除规则)
-D 删除规则 (查到规则序号后、指定链、指定序号删掉) 例: iptables -D INPUT 1 (指顶删除INPUT链的第一条规则、默认是FILTER表、如果是其他的表需要-t指定表)
-F(大写)清除所有规则,不会处理默认的规则
-X(大写)删除用户自定义的链
-Z(大写)清除链的计数器
-A (大写)添加规则到指定链的结尾
-I (大写) 添加规则到指定链的开头
-P (大写)设置默认策略的(设定默认门是关着的还是开着的)
-t 指定表(默认是filter表) 例:iptables -t filter -A INPUT -p tcp --dport 443 -j DROP #指定在filter表的input链的tcp协议443端口的请求丢弃
-p 匹配指定协议 (all(所有) 、tcp、udp、icmp(ping等)、默认all)
--dport 目的端口(对本地的端口做什么)
--sport 源端口(从那个端口连接过来的)
-j 处理的行为 {DROP(丢弃)、ACCEPT(接受)、REJECT(拒绝)、ACCEPT(接受)
-s 指定源ip地址
-d 目的地址
-i 匹配进入的网卡接口eth0或者eth1进行控制
-o 匹配出去的网卡借口

配置一个生产中只开启443端口的防火墙规则
说明:这里配置的防火墙规则只开启443端口(https),
1.首先清除防火墙的默认规则

iptables -F

iptables -X

iptables -Z

2.允许自己的网段连接服务器

iptables -A INPUT -p tcp -s 10.0.0.0/24 -j ACCEPT
允许自己的回环接口进出:
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
3.设置默认规则:

iptables -P INPUT DROP #忽略进入的请求
iptables -P FORWARD DROP #忽略要通过
iptables -P OUTPUT ACCEPT #允许出去的请求
4.配置规则

iptables -A INPUT -s 124.43.62.96/27 -p all -j ACCEPT #允许办公室的ip段连接

iptables -A INPUT -s 192.168.1.0/24 -p all -j ACCEPT #允许其他机房的网段链接

iptables -A INPUT -p tcp --dport 443 -j ACCEPT #允许业务端口对外提供链接

iptables -A INPUT -p icmp -m icmp --icmp-type any -j ACCEPT #允许所有人ping

iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT #允许关联包通过
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
至此防火墙就配完了。以上命令是在命令行执行的没有永久生效,重启主机就失效了:
永久保存的方法:

services iptables save

就保存到/etc/sysconfig/iptables中了
以后维护iptables就可以通过编辑/etc/sysconfig/iptables来更改规则了。
CentOS 7自带的防火墙是firewalld,需要下载iptables:

yum install -y iptables-services

发现问题

据说mariadb和mysql 90% 相似度,本地使用docker 起了个容器 挂载数据到本地的时候居然报错了

[ERROR] InnoDB: The Auto-extending innodb_system data file './ibdata1' is of a different size 0 pages than specified in the .cnf file: initial 768 pages, max 0 (relevant if non-zero) pages!

查询问题

和 mysql 一样的配置为什么会报错的,一脸懵逼??? ./ibdata1
查询了各大资源后发现可能是 Docker for windows 的共享文件系统或MariaDB本身中的bug!!!

解决问题

最后经过苦苦测试 终于找到了一个可行的解决方案
下面解释一下关键配置

  • ibdata1是InnoDB的共有表空间,默认情况下会把表空间存放在一个文件ibdata1中
  • innodb_flush_method 则确定日志及数据文件如何write、flush
  • innodb_flush_method可以取如下值:fdatasync, O_DSYNC, O_DIRECT 实验选项littlesync和nosync
  • innodb_use_native_aio 该选项仅适用于Linux系统,默认情况下处于启用状态。在其他类Unix系统上,InnoDB仅使用同步I/O. - 从历史上看,InnoDB仅在Windows系统上使用异步I/O. 在Linux上使用异步I/O子系统需要libaio库
  • Binary Log 简称 Binlog 即 二进制日志文件,这个文件记录了mysql所有的 DML 操作。
  • 通过 Binlog 日志我们可以做数据恢复,做主主复制和主从复制等等。

下面是的我的 docker-compose.yml 和 配置

dockerFile
FROM mariadb:latest

LABEL maintainer="Mahmoud Zalt <mahmoud@zalt.me>"

COPY my.cnf /etc/mysql/conf.d/my.cnf



CMD ["mysqld"]

EXPOSE 3306
docker-compose.yml
    mariadb:
      build: ./mariadb
      volumes:
        - ./mariadb/data:/var/lib/mysql
 
      ports:
        - "3306:3306"
      environment:
        - MYSQL_DATABASE=default
        - MYSQL_USER=default
        - MYSQL_PASSWORD=default
        - MYSQL_ROOT_PASSWORD=root
      networks:
        - backend
      command:
        mysqld --innodb-flush-method=littlesync --innodb-use-native-aio=OFF --log_bin=ON
mariadb 配置
[mysqld]
innodb_flush_method=littlesync
innodb_use_native_aio=OFF
log_bin=ON

安装包 (win10并开启虚拟化)

https://download.docker.com/win/stable/Docker%20for%20Windows%20Installer.exe

git 仓库

git@github.com:weiyixi/docker_study.git
目录说明
  1. nginx 站点配置在 ./docker-lnmp/nginx/site (修改后重启nginx容器生效)
  2. php-fpm 容器中/var/www/目录挂载本地docker-lnmp的上层目录(.env中 APPLICATION=../)
  3. 需要加扩展在对应得dockerfile 中修改

1.修改配置

env-example  复制为 .env
//window需要修改
DOCKER_SYNC_STRATEGY=unison
//php版本修改默认7.2
PHP_VERSION=72

2.基本操作

首先到docker-lnmp目录下

1.构建  docker-compose build  nginx php-fpm  redis  mysql 
2.开启  docker-compose up -d  nginx  redis  mysql 
3.关闭  docker-compose down
4.查看运行中容器  docker  ps
5.进入容器中   docker exec -it 对应的CONTAINER_ID  bash

3.扩展

1.已经定义了一批扩展可在 .env中开启 true开启 false 关闭
2.未定义的需要修改dockerfile安装
3.修改后需要重新build 开启才会生效

适用于对docker有一定了解的朋友,小白请先仔细阅读官方文档!
作为应用程序,我们通常需要依赖于多种外部服务,比如数据库、缓存服务等等。
Docker-compose就是在Docker容器的基础上,提供了统一的容器编排语言,可以让你更轻松的利用Docker构建你的应用环境。

docker-compose.yml文件语法语法

image 使用的镜像
build 指定Dockerfile构建
command 启动执行命令
links 链接其他容器    
ports 端口映射
expose 暴露端口
volumes 挂载路径
volumes_from 从容器挂载
environment 环境变量
  1. 编写dockerfile(自定义镜像)
  2. 编写docker-compose.yml(可以不依赖Dockerfile 使用官方镜像)

常用命令

构建容器 docker-compose up -d
查看容器 docker-compose ps
进入容器 docker exec -it 容器名 /bin/bash
关闭所有 docker-compose  down

php-fpm Dockerfile 示例 可以自定义扩展

FROM php:7.1-fpm-alpine

# apk
RUN apk --update add \
        autoconf \
        build-base \
        linux-headers \
        libaio-dev \
        zlib-dev \
        curl \
        git \
        subversion \
        freetype-dev \
        libjpeg-turbo-dev \
        libmcrypt-dev \
        libpng-dev \
        libtool \
        libbz2 \
        bzip2 \
        bzip2-dev \
        libstdc++ \
        libxslt-dev \
        openldap-dev \
        imagemagick-dev \
        make \
        unzip \
        wget \
        libmemcached-dev

# PHP Core Extensions
RUN docker-php-ext-install \
        bcmath \
        pdo_mysql \
        mysqli \
        pcntl \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install gd

# PECL Extensions
RUN pecl install redis-4.2.0 \
    && pecl install mongodb-1.5.3 \
    && pecl install memcached-3.1.3 \
    && pecl install swoole-4.3.1 \
    && docker-php-ext-enable redis mongodb memcached swoole

# Delete
RUN apk del build-base \
        linux-headers \
        libaio-dev \
    && rm -rf /var/cache/apk/* \
    && mkdir /data \
    && chmod -R 777 /data

VOLUME /var/www
WORKDIR /var/www

EXPOSE 9000
CMD ["php-fpm"]

docker-compose.yml示例

version: "3.0"
services:
  nginx:
    image: nginx:alpine
    ports: 
      - 80:80
      - 443:443
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ../:/var/www

  php-fpm:
    build: ./php-fpm
    ports:
        - 9000:9000
    volumes:
      - ./php-fpm/php.ini:/usr/local/etc/php/php.ini
      - ./php-fpm/www.conf:/usr/local/etc/php-fpm.d/www.conf
      - ../:/var/www

  mysql:
    image: mysql:5.7
    ports:
      - 3306:3306
    volumes:
      - ./mysql/data:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=123456

  redis:
    image: redis:5.0-alpine
    ports:
      - 6379:6379
    volumes:
      - ./redis/data:/data
      #- ./redis/redis.conf:/usr/local/etc/redis/redis.conf

  mongo:
    image: mongo
    ports: 
      - 27017:27017
    volumes:
      - ./mongo/data:/data/db
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: 123456

<script>
    /**
     * 数组中此元素出现的次数
     * @param search
     * @param array
     * @returns {number}
     */
    function in_array_count(search, array) {
        var num = 0;
        for (var i in array) {
            if (array[i] == search) {
                num++;
            }
        }
        return num;
    }

    /**
     * 数组中此元素是否出现
     * @param search
     * @param array
     * @returns {boolean}
     */
    function in_array(search, array) {
        for (var i in array) {
            if (array[i] == search) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取全部组合
     * @returns {Array}
     */
    function getProperty() {
        var property_name = ['颜色','颜色','尺寸','尺寸','尺寸','内存','内存','厚度','厚度'];         //属性名数组
        var property_value= ['红色','黑色','3.5','5.5','6.5','64G','128G','10mm','20mm'];
        var property_name_unique = [];  //去重的属性名
        var property_value_unique = []; //每个属性名对应的值  二维数组
        var property_name_val_count = [];//每个属性名对应的值个数
        var var_total_group = 1;         //组合的数量

        //所有的属性值
        for(value_key in property_value){
            if (!in_array(property_name[value_key], property_name_unique)) {
                property_name_unique.push(property_name[value_key]);//去重后的属性名
                property_value_unique[property_name[value_key]] = [];
                property_name_val_count[property_name[value_key]] = 0;
            }
            property_value_unique[property_name[value_key]].push(property_value[value_key]);//去重的属性名对应的属性值
            property_name_val_count[property_name[value_key]]++;//去重后的属性名有几个值
        };

        //总组合数 为各个属性 拥有值个数的 累乘
        for (var num_p in property_name_val_count) {
            var_total_group = property_name_val_count[num_p] * var_total_group;
        }


        var ceng = 0;           //当前第几层
        var pipei = [];         //匹配的结果值
        var ceng_old = 'X';     //上次循环时的层数
        var this_ceng_l_j = 1;  //当前层属性值个数 与 过往层个数累乘  var_total_group➗this_ceng_l_j=本层每种结果可出现重复的次数

        //根据不同属性个数 property_name_unique.length 分为 property_name_unique.length 层

        for (var time = 0; time < var_total_group * property_name_unique.length; time++) {
            ceng = Math.floor(time / var_total_group);
            if (ceng == 0) {
                //第一层初始化字符串
                pipei[time % var_total_group] = '';
            }
            //这一层拥有属性值得个数
            this_ceng_l = property_value_unique[property_name_unique[ceng]].length;

            //当前层 属性值个数 与 过往层个数 累乘
            if (ceng_old != ceng) {
                this_ceng_l_j = this_ceng_l * this_ceng_l_j;
            }

            for (var ceng_t_n = 0; ceng_t_n < this_ceng_l; ceng_t_n++) {
                temp_str = pipei[time % var_total_group] + property_name_unique[ceng] + '--' + property_value_unique[property_name_unique[ceng]][ceng_t_n] + '||';
                //这种结果可出现重复的次数是否达到最大值
                if (in_array_count(temp_str, pipei) < var_total_group / this_ceng_l_j) {
                    pipei[time % var_total_group] = temp_str;
                    break;
                }
            }
            ceng_old = ceng;
        }

        return pipei;

    }

</script>