3250 lines
263 KiB
HTML
3250 lines
263 KiB
HTML
|
<!DOCTYPE html><html lang="fr">
|
|||
|
<head><meta charset="utf-8">
|
|||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"><title>Parefeu (firewall) iptables IPV4/IPV6 - YannStatic</title>
|
|||
|
|
|||
|
<meta name="description" content="Parefeu - iptables">
|
|||
|
<link rel="canonical" href="https://static.rnmkcy.eu/2018/10/08/Pare-feu-iptables-IPv4-IPv6-versions-bureau-et-serveur.html"><link rel="alternate" type="application/rss+xml" title="YannStatic" href="/feed.xml">
|
|||
|
|
|||
|
<!-- - include head/favicon.html - -->
|
|||
|
<link rel="shortcut icon" type="image/png" href="/assets/favicon/favicon.png"><link rel="stylesheet" href="/assets/css/main.css"><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" ><!-- start custom head snippets --><link rel="stylesheet" href="/assets/css/expand.css">
|
|||
|
<!-- end custom head snippets --><script>(function() {
|
|||
|
window.isArray = function(val) {
|
|||
|
return Object.prototype.toString.call(val) === '[object Array]';
|
|||
|
};
|
|||
|
window.isString = function(val) {
|
|||
|
return typeof val === 'string';
|
|||
|
};
|
|||
|
|
|||
|
window.hasEvent = function(event) {
|
|||
|
return 'on'.concat(event) in window.document;
|
|||
|
};
|
|||
|
|
|||
|
window.isOverallScroller = function(node) {
|
|||
|
return node === document.documentElement || node === document.body || node === window;
|
|||
|
};
|
|||
|
|
|||
|
window.isFormElement = function(node) {
|
|||
|
var tagName = node.tagName;
|
|||
|
return tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA';
|
|||
|
};
|
|||
|
|
|||
|
window.pageLoad = (function () {
|
|||
|
var loaded = false, cbs = [];
|
|||
|
window.addEventListener('load', function () {
|
|||
|
var i;
|
|||
|
loaded = true;
|
|||
|
if (cbs.length > 0) {
|
|||
|
for (i = 0; i < cbs.length; i++) {
|
|||
|
cbs[i]();
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
return {
|
|||
|
then: function(cb) {
|
|||
|
cb && (loaded ? cb() : (cbs.push(cb)));
|
|||
|
}
|
|||
|
};
|
|||
|
})();
|
|||
|
})();
|
|||
|
(function() {
|
|||
|
window.throttle = function(func, wait) {
|
|||
|
var args, result, thisArg, timeoutId, lastCalled = 0;
|
|||
|
|
|||
|
function trailingCall() {
|
|||
|
lastCalled = new Date;
|
|||
|
timeoutId = null;
|
|||
|
result = func.apply(thisArg, args);
|
|||
|
}
|
|||
|
return function() {
|
|||
|
var now = new Date,
|
|||
|
remaining = wait - (now - lastCalled);
|
|||
|
|
|||
|
args = arguments;
|
|||
|
thisArg = this;
|
|||
|
|
|||
|
if (remaining <= 0) {
|
|||
|
clearTimeout(timeoutId);
|
|||
|
timeoutId = null;
|
|||
|
lastCalled = now;
|
|||
|
result = func.apply(thisArg, args);
|
|||
|
} else if (!timeoutId) {
|
|||
|
timeoutId = setTimeout(trailingCall, remaining);
|
|||
|
}
|
|||
|
return result;
|
|||
|
};
|
|||
|
};
|
|||
|
})();
|
|||
|
(function() {
|
|||
|
var Set = (function() {
|
|||
|
var add = function(item) {
|
|||
|
var i, data = this._data;
|
|||
|
for (i = 0; i < data.length; i++) {
|
|||
|
if (data[i] === item) {
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
this.size ++;
|
|||
|
data.push(item);
|
|||
|
return data;
|
|||
|
};
|
|||
|
|
|||
|
var Set = function(data) {
|
|||
|
this.size = 0;
|
|||
|
this._data = [];
|
|||
|
var i;
|
|||
|
if (data.length > 0) {
|
|||
|
for (i = 0; i < data.length; i++) {
|
|||
|
add.call(this, data[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
Set.prototype.add = add;
|
|||
|
Set.prototype.get = function(index) { return this._data[index]; };
|
|||
|
Set.prototype.has = function(item) {
|
|||
|
var i, data = this._data;
|
|||
|
for (i = 0; i < data.length; i++) {
|
|||
|
if (this.get(i) === item) {
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
return false;
|
|||
|
};
|
|||
|
Set.prototype.is = function(map) {
|
|||
|
if (map._data.length !== this._data.length) { return false; }
|
|||
|
var i, j, flag, tData = this._data, mData = map._data;
|
|||
|
for (i = 0; i < tData.length; i++) {
|
|||
|
for (flag = false, j = 0; j < mData.length; j++) {
|
|||
|
if (tData[i] === mData[j]) {
|
|||
|
flag = true;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if (!flag) { return false; }
|
|||
|
}
|
|||
|
return true;
|
|||
|
};
|
|||
|
Set.prototype.values = function() {
|
|||
|
return this._data;
|
|||
|
};
|
|||
|
return Set;
|
|||
|
})();
|
|||
|
|
|||
|
window.Lazyload = (function(doc) {
|
|||
|
var queue = {js: [], css: []}, sources = {js: {}, css: {}}, context = this;
|
|||
|
var createNode = function(name, attrs) {
|
|||
|
var node = doc.createElement(name), attr;
|
|||
|
for (attr in attrs) {
|
|||
|
if (attrs.hasOwnProperty(attr)) {
|
|||
|
node.setAttribute(attr, attrs[attr]);
|
|||
|
}
|
|||
|
}
|
|||
|
return node;
|
|||
|
};
|
|||
|
var end = function(type, url) {
|
|||
|
var s, q, qi, cbs, i, j, cur, val, flag;
|
|||
|
if (type === 'js' || type ==='css') {
|
|||
|
s = sources[type], q = queue[type];
|
|||
|
s[url] = true;
|
|||
|
for (i = 0; i < q.length; i++) {
|
|||
|
cur = q[i];
|
|||
|
if (cur.urls.has(url)) {
|
|||
|
qi = cur, val = qi.urls.values();
|
|||
|
qi && (cbs = qi.callbacks);
|
|||
|
for (flag = true, j = 0; j < val.length; j++) {
|
|||
|
cur = val[j];
|
|||
|
if (!s[cur]) {
|
|||
|
flag = false;
|
|||
|
}
|
|||
|
}
|
|||
|
if (flag && cbs && cbs.length > 0) {
|
|||
|
for (j = 0; j < cbs.length; j++) {
|
|||
|
cbs[j].call(context);
|
|||
|
}
|
|||
|
qi.load = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
var load = function(type, urls, callback) {
|
|||
|
var s, q, qi, node, i, cur,
|
|||
|
_urls = typeof urls === 'string' ? new Set([urls]) : new Set(urls), val, url;
|
|||
|
if (type === 'js' || type ==='css') {
|
|||
|
s = sources[type], q = queue[type];
|
|||
|
for (i = 0; i < q.length; i++) {
|
|||
|
cur = q[i];
|
|||
|
if (_urls.is(cur.urls)) {
|
|||
|
qi = cur;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
val = _urls.values();
|
|||
|
if (qi) {
|
|||
|
callback && (qi.load || qi.callbacks.push(callback));
|
|||
|
callback && (qi.load && callback());
|
|||
|
} else {
|
|||
|
q.push({
|
|||
|
urls: _urls,
|
|||
|
callbacks: callback ? [callback] : [],
|
|||
|
load: false
|
|||
|
});
|
|||
|
for (i = 0; i < val.length; i++) {
|
|||
|
node = null, url = val[i];
|
|||
|
if (s[url] === undefined) {
|
|||
|
(type === 'js' ) && (node = createNode('script', { src: url }));
|
|||
|
(type === 'css') && (node = createNode('link', { rel: 'stylesheet', href: url }));
|
|||
|
if (node) {
|
|||
|
node.onload = (function(type, url) {
|
|||
|
return function() {
|
|||
|
end(type, url);
|
|||
|
};
|
|||
|
})(type, url);
|
|||
|
(doc.head || doc.body).appendChild(node);
|
|||
|
s[url] = false;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
return {
|
|||
|
js: function(url, callback) {
|
|||
|
load('js', url, callback);
|
|||
|
},
|
|||
|
css: function(url, callback) {
|
|||
|
load('css', url, callback);
|
|||
|
}
|
|||
|
};
|
|||
|
})(this.document);
|
|||
|
})();
|
|||
|
</script><script>
|
|||
|
(function() {
|
|||
|
var TEXT_VARIABLES = {
|
|||
|
version: '2.2.6',
|
|||
|
sources: {
|
|||
|
font_awesome: 'https://use.fontawesome.com/releases/v5.0.13/css/all.css',
|
|||
|
jquery: '/assets/js/jquery.min.js',
|
|||
|
leancloud_js_sdk: '//cdn.jsdelivr.net/npm/leancloud-storage@3.13.2/dist/av-min.js',
|
|||
|
chart: 'https://cdn.bootcss.com/Chart.js/2.7.2/Chart.bundle.min.js',
|
|||
|
gitalk: {
|
|||
|
js: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.js',
|
|||
|
css: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.css'
|
|||
|
},
|
|||
|
valine: 'https://unpkg.com/valine/dist/Valine.min.js'
|
|||
|
},
|
|||
|
site: {
|
|||
|
toc: {
|
|||
|
selectors: 'h1,h2,h3'
|
|||
|
}
|
|||
|
},
|
|||
|
paths: {
|
|||
|
search_js: '/assets/search.js'
|
|||
|
}
|
|||
|
};
|
|||
|
window.TEXT_VARIABLES = TEXT_VARIABLES;
|
|||
|
})();
|
|||
|
</script>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<div class="root" data-is-touch="false">
|
|||
|
<div class="layout--page js-page-root">
|
|||
|
<!----><div class="page__main js-page-main page__viewport hide-footer has-aside has-aside cell cell--auto">
|
|||
|
|
|||
|
<div class="page__main-inner">
|
|||
|
<div class="page__header d-print-none">
|
|||
|
<header class="header"><div class="main">
|
|||
|
<div class="header__title">
|
|||
|
<div class="header__brand">
|
|||
|
<svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="478.9473684210526" viewbox="0, 0, 400,478.9473684210526"><g id="svgg"><path id="path0" d="M308.400 56.805 C 306.970 56.966,303.280 57.385,300.200 57.738 C 290.906 58.803,278.299 59.676,269.200 59.887 L 260.600 60.085 259.400 61.171 C 258.010 62.428,256.198 63.600,255.645 63.600 C 255.070 63.600,252.887 65.897,252.598 66.806 C 252.460 67.243,252.206 67.600,252.034 67.600 C 251.397 67.600,247.206 71.509,247.202 72.107 C 247.201 72.275,246.390 73.190,245.400 74.138 C 243.961 75.517,243.598 76.137,243.592 77.231 C 243.579 79.293,241.785 83.966,240.470 85.364 C 239.176 86.740,238.522 88.365,237.991 91.521 C 237.631 93.665,236.114 97.200,235.554 97.200 C 234.938 97.200,232.737 102.354,232.450 104.472 C 232.158 106.625,230.879 109.226,229.535 110.400 C 228.933 110.926,228.171 113.162,226.434 119.500 C 226.178 120.435,225.795 121.200,225.584 121.200 C 225.373 121.200,225.200 121.476,225.200 121.813 C 225.200 122.149,224.885 122.541,224.500 122.683 C 223.606 123.013,223.214 123.593,223.204 124.600 C 223.183 126.555,220.763 132.911,219.410 134.562 C 218.443 135.742,217.876 136.956,217.599 138.440 C 217.041 141.424,215.177 146.434,214.532 146.681 C 214.240 146.794,214.000 147.055,214.000 147.261 C 214.000 147.467,213.550 148.086,213.000 148.636 C 212.450 149.186,212.000 149.893,212.000 150.208 C 212.000 151.386,208.441 154.450,207.597 153.998 C 206.319 153.315,204.913 150.379,204.633 147.811 C 204.365 145.357,202.848 142.147,201.759 141.729 C 200.967 141.425,199.200 137.451,199.200 135.974 C 199.200 134.629,198.435 133.224,196.660 131.311 C 195.363 129.913,194.572 128.123,193.870 125.000 C 193.623 123.900,193.236 122.793,193.010 122.540 C 190.863 120.133,190.147 118.880,188.978 115.481 C 188.100 112.928,187.151 111.003,186.254 109.955 C 185.358 108.908,184.518 107.204,183.847 105.073 C 183.280 103.273,182.497 101.329,182.108 100.753 C 181.719 100.177,180.904 98.997,180.298 98.131 C 179.693 97.265,178.939 95.576,178.624 94.378 C 178.041 92.159,177.125 90.326,175.023 87.168 C 174.375 86.196,173.619 84.539,173.342 83.486 C 172.800 81.429,171.529 79.567,170.131 78.785 C 169.654 78.517,168.697 77.511,168.006 76.549 C 167.316 75.587,166.594 74.800,166.402 74.800 C 166.210 74.800,164.869 73.633,163.421 72.206 C 160.103 68.936,161.107 69.109,146.550 69.301 C 133.437 69.474,128.581 70.162,126.618 72.124 C 126.248 72.495,125.462 72.904,124.872 73.033 C 124.282 73.163,123.088 73.536,122.219 73.863 C 121.349 74.191,119.028 74.638,117.061 74.858 C 113.514 75.254,109.970 76.350,108.782 77.419 C 107.652 78.436,100.146 80.400,97.388 80.400 C 95.775 80.400,93.167 81.360,91.200 82.679 C 90.430 83.195,89.113 83.804,88.274 84.031 C 85.875 84.681,78.799 90.910,74.400 96.243 L 73.400 97.456 73.455 106.028 C 73.526 117.055,74.527 121.238,77.820 124.263 C 78.919 125.273,80.400 127.902,80.400 128.842 C 80.400 129.202,81.075 130.256,81.900 131.186 C 83.563 133.059,85.497 136.346,86.039 138.216 C 86.233 138.886,87.203 140.207,88.196 141.153 C 89.188 142.098,90.000 143.104,90.000 143.388 C 90.000 144.337,92.129 148.594,92.869 149.123 C 93.271 149.410,93.600 149.831,93.600 150.059 C 93.600 150.286,93.932 150.771,94.337 151.136 C 94.743 151.501,95.598 153.004,96.237 154.475 C 96.877 155.947,97.760 157.351,98.200 157.596 C 98.640 157.841,99.900 159.943,101.000 162.267 C 102.207 164.817,103.327 166.644,103.825 166.876 C 104.278 167.087,105.065 168.101,105.573 169.130 C 107.658 173.348,108.097 174.093,110.006 176.647 C 111.103 178.114,112.000 179.725,112.000 180.227 C 112.000 181.048,113.425 183.163,114.678 184.200 C 115.295 184.711,117.396 188.733,117.720 190.022 C 117.855 190.562,118.603 191.633,119.381 192.402 C 120.160 193.171,121.496 195.258,122.351 197.039 C 123.206 198.820,124.167 200.378,124.487 200.501 C 124.807 200.624,125.953 202.496,127.034 204.662 C 128.114 206.828,129.676 209.299,130.505 210.153 C 131.333 211.007,132.124 212.177,132.262 212.753 C 132.618 214.239,134.291 217.048,136.288 219.516 C 137.230 220.679,138.000 221.92
|
|||
|
" href="/">YannStatic</a>
|
|||
|
</div>
|
|||
|
<!--<button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button>--><!-- <li><button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button></li> -->
|
|||
|
<!-- Champ de recherche -->
|
|||
|
<div id="searchbox" class="search search--dark" style="visibility: visible">
|
|||
|
<div class="main">
|
|||
|
<div class="search__header"></div>
|
|||
|
<div class="search-bar">
|
|||
|
<div class="search-box js-search-box">
|
|||
|
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
|
|||
|
<input id="search-input" type="text">
|
|||
|
<!-- <div class="search-box__icon-clear js-icon-clear">
|
|||
|
<a><i class="fas fa-times"></i></a>
|
|||
|
</div> -->
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<!-- Script pointing to search-script.js -->
|
|||
|
<script>/*!
|
|||
|
* Simple-Jekyll-Search
|
|||
|
* Copyright 2015-2020, Christian Fei
|
|||
|
* Licensed under the MIT License.
|
|||
|
*/
|
|||
|
|
|||
|
(function(){
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$Templater_7 = {
|
|||
|
compile: compile,
|
|||
|
setOptions: setOptions
|
|||
|
}
|
|||
|
|
|||
|
const options = {}
|
|||
|
options.pattern = /\{(.*?)\}/g
|
|||
|
options.template = ''
|
|||
|
options.middleware = function () {}
|
|||
|
|
|||
|
function setOptions (_options) {
|
|||
|
options.pattern = _options.pattern || options.pattern
|
|||
|
options.template = _options.template || options.template
|
|||
|
if (typeof _options.middleware === 'function') {
|
|||
|
options.middleware = _options.middleware
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function compile (data) {
|
|||
|
return options.template.replace(options.pattern, function (match, prop) {
|
|||
|
const value = options.middleware(prop, data[prop], options.template)
|
|||
|
if (typeof value !== 'undefined') {
|
|||
|
return value
|
|||
|
}
|
|||
|
return data[prop] || match
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
'use strict';
|
|||
|
|
|||
|
function fuzzysearch (needle, haystack) {
|
|||
|
var tlen = haystack.length;
|
|||
|
var qlen = needle.length;
|
|||
|
if (qlen > tlen) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
if (qlen === tlen) {
|
|||
|
return needle === haystack;
|
|||
|
}
|
|||
|
outer: for (var i = 0, j = 0; i < qlen; i++) {
|
|||
|
var nch = needle.charCodeAt(i);
|
|||
|
while (j < tlen) {
|
|||
|
if (haystack.charCodeAt(j++) === nch) {
|
|||
|
continue outer;
|
|||
|
}
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
var _$fuzzysearch_1 = fuzzysearch;
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
|
|||
|
|
|||
|
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
|
|||
|
|
|||
|
function FuzzySearchStrategy () {
|
|||
|
this.matches = function (string, crit) {
|
|||
|
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
|
|||
|
|
|||
|
function LiteralSearchStrategy () {
|
|||
|
this.matches = function (str, crit) {
|
|||
|
if (!str) return false
|
|||
|
|
|||
|
str = str.trim().toLowerCase()
|
|||
|
crit = crit.trim().toLowerCase()
|
|||
|
|
|||
|
return crit.split(' ').filter(function (word) {
|
|||
|
return str.indexOf(word) >= 0
|
|||
|
}).length === crit.split(' ').length
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$Repository_4 = {
|
|||
|
put: put,
|
|||
|
clear: clear,
|
|||
|
search: search,
|
|||
|
setOptions: __setOptions_4
|
|||
|
}
|
|||
|
|
|||
|
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
|
|||
|
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
|
|||
|
|
|||
|
function NoSort () {
|
|||
|
return 0
|
|||
|
}
|
|||
|
|
|||
|
const data = []
|
|||
|
let opt = {}
|
|||
|
|
|||
|
opt.fuzzy = false
|
|||
|
opt.limit = 10
|
|||
|
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
|
|||
|
opt.sort = NoSort
|
|||
|
opt.exclude = []
|
|||
|
|
|||
|
function put (data) {
|
|||
|
if (isObject(data)) {
|
|||
|
return addObject(data)
|
|||
|
}
|
|||
|
if (isArray(data)) {
|
|||
|
return addArray(data)
|
|||
|
}
|
|||
|
return undefined
|
|||
|
}
|
|||
|
function clear () {
|
|||
|
data.length = 0
|
|||
|
return data
|
|||
|
}
|
|||
|
|
|||
|
function isObject (obj) {
|
|||
|
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
|
|||
|
}
|
|||
|
|
|||
|
function isArray (obj) {
|
|||
|
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
|
|||
|
}
|
|||
|
|
|||
|
function addObject (_data) {
|
|||
|
data.push(_data)
|
|||
|
return data
|
|||
|
}
|
|||
|
|
|||
|
function addArray (_data) {
|
|||
|
const added = []
|
|||
|
clear()
|
|||
|
for (let i = 0, len = _data.length; i < len; i++) {
|
|||
|
if (isObject(_data[i])) {
|
|||
|
added.push(addObject(_data[i]))
|
|||
|
}
|
|||
|
}
|
|||
|
return added
|
|||
|
}
|
|||
|
|
|||
|
function search (crit) {
|
|||
|
if (!crit) {
|
|||
|
return []
|
|||
|
}
|
|||
|
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
|
|||
|
}
|
|||
|
|
|||
|
function __setOptions_4 (_opt) {
|
|||
|
opt = _opt || {}
|
|||
|
|
|||
|
opt.fuzzy = _opt.fuzzy || false
|
|||
|
opt.limit = _opt.limit || 10
|
|||
|
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
|
|||
|
opt.sort = _opt.sort || NoSort
|
|||
|
opt.exclude = _opt.exclude || []
|
|||
|
}
|
|||
|
|
|||
|
function findMatches (data, crit, strategy, opt) {
|
|||
|
const matches = []
|
|||
|
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
|
|||
|
const match = findMatchesInObject(data[i], crit, strategy, opt)
|
|||
|
if (match) {
|
|||
|
matches.push(match)
|
|||
|
}
|
|||
|
}
|
|||
|
return matches
|
|||
|
}
|
|||
|
|
|||
|
function findMatchesInObject (obj, crit, strategy, opt) {
|
|||
|
for (const key in obj) {
|
|||
|
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
|
|||
|
return obj
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function isExcluded (term, excludedTerms) {
|
|||
|
for (let i = 0, len = excludedTerms.length; i < len; i++) {
|
|||
|
const excludedTerm = excludedTerms[i]
|
|||
|
if (new RegExp(excludedTerm).test(term)) {
|
|||
|
return true
|
|||
|
}
|
|||
|
}
|
|||
|
return false
|
|||
|
}
|
|||
|
|
|||
|
/* globals ActiveXObject:false */
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$JSONLoader_2 = {
|
|||
|
load: load
|
|||
|
}
|
|||
|
|
|||
|
function load (location, callback) {
|
|||
|
const xhr = getXHR()
|
|||
|
xhr.open('GET', location, true)
|
|||
|
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
|
|||
|
xhr.send()
|
|||
|
}
|
|||
|
|
|||
|
function createStateChangeListener (xhr, callback) {
|
|||
|
return function () {
|
|||
|
if (xhr.readyState === 4 && xhr.status === 200) {
|
|||
|
try {
|
|||
|
callback(null, JSON.parse(xhr.responseText))
|
|||
|
} catch (err) {
|
|||
|
callback(err, null)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function getXHR () {
|
|||
|
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$OptionsValidator_3 = function OptionsValidator (params) {
|
|||
|
if (!validateParams(params)) {
|
|||
|
throw new Error('-- OptionsValidator: required options missing')
|
|||
|
}
|
|||
|
|
|||
|
if (!(this instanceof OptionsValidator)) {
|
|||
|
return new OptionsValidator(params)
|
|||
|
}
|
|||
|
|
|||
|
const requiredOptions = params.required
|
|||
|
|
|||
|
this.getRequiredOptions = function () {
|
|||
|
return requiredOptions
|
|||
|
}
|
|||
|
|
|||
|
this.validate = function (parameters) {
|
|||
|
const errors = []
|
|||
|
requiredOptions.forEach(function (requiredOptionName) {
|
|||
|
if (typeof parameters[requiredOptionName] === 'undefined') {
|
|||
|
errors.push(requiredOptionName)
|
|||
|
}
|
|||
|
})
|
|||
|
return errors
|
|||
|
}
|
|||
|
|
|||
|
function validateParams (params) {
|
|||
|
if (!params) {
|
|||
|
return false
|
|||
|
}
|
|||
|
return typeof params.required !== 'undefined' && params.required instanceof Array
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$utils_9 = {
|
|||
|
merge: merge,
|
|||
|
isJSON: isJSON
|
|||
|
}
|
|||
|
|
|||
|
function merge (defaultParams, mergeParams) {
|
|||
|
const mergedOptions = {}
|
|||
|
for (const option in defaultParams) {
|
|||
|
mergedOptions[option] = defaultParams[option]
|
|||
|
if (typeof mergeParams[option] !== 'undefined') {
|
|||
|
mergedOptions[option] = mergeParams[option]
|
|||
|
}
|
|||
|
}
|
|||
|
return mergedOptions
|
|||
|
}
|
|||
|
|
|||
|
function isJSON (json) {
|
|||
|
try {
|
|||
|
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
|
|||
|
return true
|
|||
|
}
|
|||
|
return false
|
|||
|
} catch (err) {
|
|||
|
return false
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var _$src_8 = {};
|
|||
|
(function (window) {
|
|||
|
'use strict'
|
|||
|
|
|||
|
let options = {
|
|||
|
searchInput: null,
|
|||
|
resultsContainer: null,
|
|||
|
json: [],
|
|||
|
success: Function.prototype,
|
|||
|
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
|
|||
|
templateMiddleware: Function.prototype,
|
|||
|
sortMiddleware: function () {
|
|||
|
return 0
|
|||
|
},
|
|||
|
noResultsText: 'No results found',
|
|||
|
limit: 10,
|
|||
|
fuzzy: false,
|
|||
|
debounceTime: null,
|
|||
|
exclude: []
|
|||
|
}
|
|||
|
|
|||
|
let debounceTimerHandle
|
|||
|
const debounce = function (func, delayMillis) {
|
|||
|
if (delayMillis) {
|
|||
|
clearTimeout(debounceTimerHandle)
|
|||
|
debounceTimerHandle = setTimeout(func, delayMillis)
|
|||
|
} else {
|
|||
|
func.call()
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
|
|||
|
|
|||
|
/* removed: const _$Templater_7 = require('./Templater') */;
|
|||
|
/* removed: const _$Repository_4 = require('./Repository') */;
|
|||
|
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
|
|||
|
const optionsValidator = _$OptionsValidator_3({
|
|||
|
required: requiredOptions
|
|||
|
})
|
|||
|
/* removed: const _$utils_9 = require('./utils') */;
|
|||
|
|
|||
|
window.SimpleJekyllSearch = function (_options) {
|
|||
|
const errors = optionsValidator.validate(_options)
|
|||
|
if (errors.length > 0) {
|
|||
|
throwError('You must specify the following required options: ' + requiredOptions)
|
|||
|
}
|
|||
|
|
|||
|
options = _$utils_9.merge(options, _options)
|
|||
|
|
|||
|
_$Templater_7.setOptions({
|
|||
|
template: options.searchResultTemplate,
|
|||
|
middleware: options.templateMiddleware
|
|||
|
})
|
|||
|
|
|||
|
_$Repository_4.setOptions({
|
|||
|
fuzzy: options.fuzzy,
|
|||
|
limit: options.limit,
|
|||
|
sort: options.sortMiddleware,
|
|||
|
exclude: options.exclude
|
|||
|
})
|
|||
|
|
|||
|
if (_$utils_9.isJSON(options.json)) {
|
|||
|
initWithJSON(options.json)
|
|||
|
} else {
|
|||
|
initWithURL(options.json)
|
|||
|
}
|
|||
|
|
|||
|
const rv = {
|
|||
|
search: search
|
|||
|
}
|
|||
|
|
|||
|
typeof options.success === 'function' && options.success.call(rv)
|
|||
|
return rv
|
|||
|
}
|
|||
|
|
|||
|
function initWithJSON (json) {
|
|||
|
_$Repository_4.put(json)
|
|||
|
registerInput()
|
|||
|
}
|
|||
|
|
|||
|
function initWithURL (url) {
|
|||
|
_$JSONLoader_2.load(url, function (err, json) {
|
|||
|
if (err) {
|
|||
|
throwError('failed to get JSON (' + url + ')')
|
|||
|
}
|
|||
|
initWithJSON(json)
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
function emptyResultsContainer () {
|
|||
|
options.resultsContainer.innerHTML = ''
|
|||
|
}
|
|||
|
|
|||
|
function appendToResultsContainer (text) {
|
|||
|
options.resultsContainer.innerHTML += text
|
|||
|
}
|
|||
|
|
|||
|
function registerInput () {
|
|||
|
options.searchInput.addEventListener('input', function (e) {
|
|||
|
if (isWhitelistedKey(e.which)) {
|
|||
|
emptyResultsContainer()
|
|||
|
debounce(function () { search(e.target.value) }, options.debounceTime)
|
|||
|
}
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
function search (query) {
|
|||
|
if (isValidQuery(query)) {
|
|||
|
emptyResultsContainer()
|
|||
|
render(_$Repository_4.search(query), query)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function render (results, query) {
|
|||
|
const len = results.length
|
|||
|
if (len === 0) {
|
|||
|
return appendToResultsContainer(options.noResultsText)
|
|||
|
}
|
|||
|
for (let i = 0; i < len; i++) {
|
|||
|
results[i].query = query
|
|||
|
appendToResultsContainer(_$Templater_7.compile(results[i]))
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function isValidQuery (query) {
|
|||
|
return query && query.length > 0
|
|||
|
}
|
|||
|
|
|||
|
function isWhitelistedKey (key) {
|
|||
|
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
|
|||
|
}
|
|||
|
|
|||
|
function throwError (message) {
|
|||
|
throw new Error('SimpleJekyllSearch --- ' + message)
|
|||
|
}
|
|||
|
})(window)
|
|||
|
|
|||
|
}());
|
|||
|
</script>
|
|||
|
|
|||
|
<!-- Configuration -->
|
|||
|
<script>
|
|||
|
SimpleJekyllSearch({
|
|||
|
searchInput: document.getElementById('search-input'),
|
|||
|
resultsContainer: document.getElementById('results-container'),
|
|||
|
json: '/search.json',
|
|||
|
//searchResultTemplate: '<li><a href="https://static.rnmkcy.eu{url}">{date} {title}</a></li>'
|
|||
|
searchResultTemplate: '<li><a href="{url}">{date} {title}</a></li>'
|
|||
|
})
|
|||
|
</script>
|
|||
|
<!-- Fin déclaration champ de recherche -->
|
|||
|
</div>
|
|||
|
<nav class="navigation">
|
|||
|
<ul>
|
|||
|
<li class="navigation__item"><a href="/archive.html">Etiquettes</a></li>
|
|||
|
<li class="navigation__item"><a href="/htmldoc.html">Documents</a></li>
|
|||
|
<li class="navigation__item"><a href="/liens_ttrss.html">Liens</a></li>
|
|||
|
<li class="navigation__item"><a href="/aide-jekyll-text-theme.html">Aide</a></li>
|
|||
|
</ul>
|
|||
|
</nav>
|
|||
|
</div>
|
|||
|
</header>
|
|||
|
|
|||
|
</div>
|
|||
|
<div class="page__content"><div class="main"><div class="grid grid--reverse">
|
|||
|
<div class="col-main cell cell--auto">
|
|||
|
<!-- start custom main top snippet --><div id="results-container" class="search-result js-search-result"></div>
|
|||
|
<!-- end custom main top snippet -->
|
|||
|
<article itemscope itemtype="http://schema.org/Article"><div class="article__header"><header><h1 style="color:Tomato;">Parefeu (firewall) iptables IPV4/IPV6</h1></header></div>
|
|||
|
<meta itemprop="headline" content="Parefeu (firewall) iptables IPV4/IPV6">
|
|||
|
<div class="article__info clearfix">
|
|||
|
<ul class="left-col menu"><li>
|
|||
|
<a class="button button--secondary button--pill button--sm" href="/archive.html?tag=parefeu">parefeu</a>
|
|||
|
</li></ul>
|
|||
|
<ul class="right-col menu"><li>
|
|||
|
<i class="far fa-calendar-alt"></i> <span title="Création" style="color:#FF00FF"> 8 oct. 2018</span>
|
|||
|
|
|||
|
<span title="Modification" style="color:#00FF7F">20 avr. 2020</span>
|
|||
|
</li></ul>
|
|||
|
</div>
|
|||
|
<meta itemprop="datePublished" content="2020-04-20T00:00:00+02:00">
|
|||
|
<meta itemprop="keywords" content="parefeu">
|
|||
|
<div class="js-article-content">
|
|||
|
<div class="layout--article">
|
|||
|
<!-- start custom article top snippet -->
|
|||
|
<style>
|
|||
|
#myBtn {
|
|||
|
display: none;
|
|||
|
position: fixed;
|
|||
|
bottom: 10px;
|
|||
|
right: 10px;
|
|||
|
z-index: 99;
|
|||
|
font-size: 12px;
|
|||
|
font-weight: bold;
|
|||
|
border: none;
|
|||
|
outline: none;
|
|||
|
background-color: white;
|
|||
|
color: black;
|
|||
|
cursor: pointer;
|
|||
|
padding: 5px;
|
|||
|
border-radius: 4px;
|
|||
|
}
|
|||
|
|
|||
|
#myBtn:hover {
|
|||
|
background-color: #555;
|
|||
|
}
|
|||
|
</style>
|
|||
|
|
|||
|
<button onclick="topFunction()" id="myBtn" title="Haut de page">⇧</button>
|
|||
|
|
|||
|
<script>
|
|||
|
//Get the button
|
|||
|
var mybutton = document.getElementById("myBtn");
|
|||
|
|
|||
|
// When the user scrolls down 20px from the top of the document, show the button
|
|||
|
window.onscroll = function() {scrollFunction()};
|
|||
|
|
|||
|
function scrollFunction() {
|
|||
|
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
|
|||
|
mybutton.style.display = "block";
|
|||
|
} else {
|
|||
|
mybutton.style.display = "none";
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// When the user clicks on the button, scroll to the top of the document
|
|||
|
function topFunction() {
|
|||
|
document.body.scrollTop = 0;
|
|||
|
document.documentElement.scrollTop = 0;
|
|||
|
}
|
|||
|
</script>
|
|||
|
|
|||
|
|
|||
|
<!-- end custom article top snippet -->
|
|||
|
<div class="article__content" itemprop="articleBody">
|
|||
|
<details>
|
|||
|
<summary><b>Afficher/cacher Sommaire</b></summary>
|
|||
|
<!-- affichage sommaire -->
|
|||
|
<div class="toc-aside js-toc-root"></div>
|
|||
|
</details><h1 id="parefeu---iptables">Parefeu - iptables</h1>
|
|||
|
|
|||
|
<p><em>Un pare-feu (de l’anglais firewall) est un logiciel et/ou un matériel permettant de faire respecter la politique de sécurité du réseau, celle-ci définissant quels sont les types de communications autorisés sur ce réseau informatique. Il surveille et contrôle les applications et les flux de données (paquets).<a href="https://fr.wikipedia.org/wiki/Pare-feu_(informatique)">Définition Wikipédia</a></em></p>
|
|||
|
|
|||
|
<p><img src="/images/iptables-logo.png" alt="parefeu"></p>
|
|||
|
|
|||
|
<p><strong>Article original :</strong> <a href="https://www.debian-fr.org/t/pare-feu-ipv4-ipv6-versions-bureau-et-serveur/68665">Pare-feu IPv4/IPv6, versions bureau et serveur</a></p>
|
|||
|
|
|||
|
<p>Sur Debian, iptables et ip6tables (tous deux empaquetés dans le paquet iptables) vous permettront de mettre assez facilement en place un pare-feu basique pour IPv4 et IPv6.<br>
|
|||
|
Après installation, le pare-feu est ouvert , une configuration est nécessaire.<br>
|
|||
|
Le lancement du pare-feu, avec sa configuration, se fera au démarrage de la machine.</p>
|
|||
|
|
|||
|
<p><strong>IPv4/IPv6</strong> Une adresse IP sert à identifier une machine dans un réseau. Deux normes coexistent ,IPV4 et IPV6 (plus certaines spécificités) , pour une protction sur les deux fronts.</p>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p><em>iptables</em> est normalement installé par défaut sur les serveurs debian.</p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p>Dans le cas contraire</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt install iptables # debian/ubuntu
|
|||
|
sudo pacman -S iptables # archlinux/manjaro
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h2 id="iptables---systemd">Iptables - systemd</h2>
|
|||
|
|
|||
|
<h3 id="script--iptables-firewallsh">script “iptables-firewall.sh”</h3>
|
|||
|
|
|||
|
<p>On utilise un service systemd</p>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p><strong>ATTENTION NE PAS OUBLIER DE CHANGER LE PORT SSH ET SUPPRIMER LES REGLES Postfix SMTP, SMTPS, SUBMISSION et IMAPS</strong></p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p>Créer le fichier le script <strong>/sbin/iptables-firewall.sh</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /sbin/iptables-firewall.sh
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/bin/bash
|
|||
|
# Configure iptables firewall
|
|||
|
|
|||
|
# Limit PATH
|
|||
|
PATH="/sbin:/usr/sbin:/bin:/usr/bin"
|
|||
|
|
|||
|
# iptables configuration
|
|||
|
firewall_start() {
|
|||
|
|
|||
|
###################
|
|||
|
# IPv4 #
|
|||
|
###################
|
|||
|
|
|||
|
# refuser input ,forward et output par défaut
|
|||
|
iptables -t filter -P INPUT DROP
|
|||
|
iptables -t filter -P FORWARD DROP
|
|||
|
iptables -t filter -P OUTPUT DROP
|
|||
|
|
|||
|
# Autoriser loopback
|
|||
|
iptables -t filter -A INPUT -i lo -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -o lo -j ACCEPT
|
|||
|
|
|||
|
# ICMP (Ping)
|
|||
|
iptables -t filter -A INPUT -p icmp -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p icmp -j ACCEPT
|
|||
|
|
|||
|
# maintenir les connexions établies
|
|||
|
iptables -t filter -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
|
|||
|
|
|||
|
# SSH In/Out
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 55034 -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 55034 -j ACCEPT
|
|||
|
|
|||
|
# HTTP + HTTPS Out
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 80 -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 443 -j ACCEPT
|
|||
|
|
|||
|
# HTTP + HTTPS In
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 80 -j ACCEPT
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 443 -j ACCEPT
|
|||
|
|
|||
|
# DNS In/Out
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 53 -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p udp --dport 53 -j ACCEPT
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 53 -j ACCEPT
|
|||
|
iptables -t filter -A INPUT -p udp --dport 53 -j ACCEPT
|
|||
|
|
|||
|
# Postfix SMTP, SMTPS, SUBMISSION et IMAPS
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 25 -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 25 -j ACCEPT
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 465 -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 465 -j ACCEPT
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 587 -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 587 -j ACCEPT
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 993 -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 993 -j ACCEPT
|
|||
|
|
|||
|
|
|||
|
###################
|
|||
|
# IPv6 #
|
|||
|
###################
|
|||
|
|
|||
|
# refuser input , forward et output par défaut
|
|||
|
ip6tables -t filter -P INPUT DROP
|
|||
|
ip6tables -t filter -P FORWARD DROP
|
|||
|
ip6tables -t filter -P OUTPUT DROP
|
|||
|
|
|||
|
# interface lo (loop) accessible
|
|||
|
ip6tables -t filter -A INPUT -i lo -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -o lo -j ACCEPT
|
|||
|
|
|||
|
# maintenir les connexions établies
|
|||
|
ip6tables -t filter -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
|
|||
|
|
|||
|
# NDP pour toute interface de type broadcast
|
|||
|
ip6tables -t filter -A INPUT -p icmpv6 --icmpv6-type neighbour-solicitation -m hl --hl-eq 255 -j ACCEPT
|
|||
|
ip6tables -t filter -A INPUT -p icmpv6 --icmpv6-type neighbour-advertisement -m hl --hl-eq 255 -j ACCEPT
|
|||
|
ip6tables -t filter -A INPUT -p icmpv6 --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT
|
|||
|
|
|||
|
ip6tables -t filter -A OUTPUT -p icmpv6 --icmpv6-type neighbour-solicitation -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p icmpv6 --icmpv6-type neighbour-advertisement -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p icmpv6 --icmpv6-type router-solicitation -j ACCEPT
|
|||
|
|
|||
|
# accepter en entrée le ping (icmpv6), les
|
|||
|
# connexions entrantes déjà établies et les connexions sur les ports nécessaires.
|
|||
|
ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -m conntrack --ctstate NEW -m limit --limit 1/s --limit-burst 1 -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT
|
|||
|
|
|||
|
# SSH In/Out
|
|||
|
ip6tables -t filter -A INPUT -p tcp --dport 55034 -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p tcp --dport 55034 -j ACCEPT
|
|||
|
|
|||
|
# HTTP + HTTPS Out
|
|||
|
ip6tables -t filter -A OUTPUT -p tcp --dport 80 -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p tcp --dport 443 -j ACCEPT
|
|||
|
|
|||
|
# HTTP + HTTPS In
|
|||
|
ip6tables -t filter -A INPUT -p tcp --dport 80 -j ACCEPT
|
|||
|
ip6tables -t filter -A INPUT -p tcp --dport 443 -j ACCEPT
|
|||
|
|
|||
|
# DNS In/Out
|
|||
|
ip6tables -t filter -A OUTPUT -p tcp --dport 53 -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p udp --dport 53 -j ACCEPT
|
|||
|
ip6tables -t filter -A INPUT -p tcp --dport 53 -j ACCEPT
|
|||
|
ip6tables -t filter -A INPUT -p udp --dport 53 -j ACCEPT
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
# clear iptables configuration
|
|||
|
firewall_stop() {
|
|||
|
iptables -F
|
|||
|
iptables -X
|
|||
|
iptables -P INPUT ACCEPT
|
|||
|
iptables -P FORWARD ACCEPT
|
|||
|
iptables -P OUTPUT ACCEPT
|
|||
|
ip6tables -F
|
|||
|
ip6tables -X
|
|||
|
ip6tables -P INPUT ACCEPT
|
|||
|
ip6tables -P FORWARD ACCEPT
|
|||
|
ip6tables -P OUTPUT ACCEPT
|
|||
|
}
|
|||
|
|
|||
|
# execute action
|
|||
|
case "$1" in
|
|||
|
start|restart)
|
|||
|
echo "Starting firewall"
|
|||
|
firewall_stop
|
|||
|
firewall_start
|
|||
|
;;
|
|||
|
stop)
|
|||
|
echo "Stopping firewall"
|
|||
|
firewall_stop
|
|||
|
;;
|
|||
|
esac
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Les droits et exécutable</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo chown root:root /sbin/iptables-firewall.sh
|
|||
|
sudo chmod 750 /sbin/iptables-firewall.sh
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="service-systemd-iptables-firewallservice">Service systemd “iptables-firewall.service”</h3>
|
|||
|
|
|||
|
<p>Créer le service systemd <strong>iptables-firewall.service</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cat << EOF | sudo tee /etc/systemd/system/iptables-firewall.service
|
|||
|
[Unit]
|
|||
|
Description=iptables firewall service
|
|||
|
After=network.target
|
|||
|
|
|||
|
[Service]
|
|||
|
Type=oneshot
|
|||
|
ExecStart=/sbin/iptables-firewall.sh start
|
|||
|
RemainAfterExit=true
|
|||
|
ExecStop=/sbin/iptables-firewall.sh stop
|
|||
|
StandardOutput=journal
|
|||
|
|
|||
|
[Install]
|
|||
|
WantedBy=multi-user.target
|
|||
|
EOF
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="lancement-et-activation-service">Lancement et activation service</h3>
|
|||
|
|
|||
|
<p>Recharger systemd manager</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl daemon-reload
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Lancer le service iptables et l’activer</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl start iptables-firewall
|
|||
|
sudo systemctl enable iptables-firewall
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h2 id="iptables---debian-paquet-iptables-persistent">Iptables - Debian paquet iptables-persistent</h2>
|
|||
|
|
|||
|
<h3 id="script-pour-initialiser-les-règles-iptables">Script pour initialiser les règles iptables</h3>
|
|||
|
|
|||
|
<p>Initialiser les règles de base <strong>iptables</strong></p>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p><strong>ATTENTION !!! NE PAS OUBLIER DE MODIFIER LE PORT SSH SI DIFFERENT DE 22</strong></p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p>Le script de base <strong>/usr/local/sbin/config_firewall</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /usr/local/sbin/config_firewall
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/bin/sh
|
|||
|
|
|||
|
###################
|
|||
|
# IPv4 #
|
|||
|
###################
|
|||
|
|
|||
|
# refuser input ,forward et output par défaut
|
|||
|
iptables -t filter -P INPUT DROP
|
|||
|
iptables -t filter -P FORWARD DROP
|
|||
|
iptables -t filter -P OUTPUT DROP
|
|||
|
|
|||
|
# Autoriser loopback
|
|||
|
iptables -t filter -A INPUT -i lo -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -o lo -j ACCEPT
|
|||
|
|
|||
|
# ICMP (Ping)
|
|||
|
iptables -t filter -A INPUT -p icmp -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p icmp -j ACCEPT
|
|||
|
|
|||
|
# maintenir les connexions établies
|
|||
|
iptables -t filter -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
|
|||
|
|
|||
|
# SSH In/Out
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 22 -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 22 -j ACCEPT
|
|||
|
|
|||
|
# HTTP + HTTPS Out
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 80 -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 443 -j ACCEPT
|
|||
|
|
|||
|
# HTTP + HTTPS In
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 80 -j ACCEPT
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 443 -j ACCEPT
|
|||
|
|
|||
|
# DNS In/Out
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 53 -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p udp --dport 53 -j ACCEPT
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 53 -j ACCEPT
|
|||
|
iptables -t filter -A INPUT -p udp --dport 53 -j ACCEPT
|
|||
|
|
|||
|
|
|||
|
###################
|
|||
|
# IPv6 #
|
|||
|
###################
|
|||
|
|
|||
|
# refuser input , forward et output par défaut
|
|||
|
ip6tables -t filter -P INPUT DROP
|
|||
|
ip6tables -t filter -P FORWARD DROP
|
|||
|
ip6tables -t filter -P OUTPUT DROP
|
|||
|
|
|||
|
# interface lo (loop) accessible
|
|||
|
ip6tables -t filter -A INPUT -i lo -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -o lo -j ACCEPT
|
|||
|
|
|||
|
# maintenir les connexions établies
|
|||
|
ip6tables -t filter -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
|
|||
|
|
|||
|
# NDP pour toute interface de type broadcast
|
|||
|
ip6tables -t filter -A INPUT -p icmpv6 --icmpv6-type neighbour-solicitation -m hl --hl-eq 255 -j ACCEPT
|
|||
|
ip6tables -t filter -A INPUT -p icmpv6 --icmpv6-type neighbour-advertisement -m hl --hl-eq 255 -j ACCEPT
|
|||
|
ip6tables -t filter -A INPUT -p icmpv6 --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT
|
|||
|
|
|||
|
ip6tables -t filter -A OUTPUT -p icmpv6 --icmpv6-type neighbour-solicitation -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p icmpv6 --icmpv6-type neighbour-advertisement -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p icmpv6 --icmpv6-type router-solicitation -j ACCEPT
|
|||
|
|
|||
|
# accepter en entrée le ping (icmpv6), les
|
|||
|
# connexions entrantes déjà établies et les connexions sur les ports nécessaires.
|
|||
|
ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -m conntrack --ctstate NEW -m limit --limit 1/s --limit-burst 1 -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT
|
|||
|
|
|||
|
# SSH In/Out
|
|||
|
ip6tables -t filter -A INPUT -p tcp --dport 22 -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p tcp --dport 22 -j ACCEPT
|
|||
|
|
|||
|
# HTTP + HTTPS Out
|
|||
|
ip6tables -t filter -A OUTPUT -p tcp --dport 80 -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p tcp --dport 443 -j ACCEPT
|
|||
|
|
|||
|
# HTTP + HTTPS In
|
|||
|
ip6tables -t filter -A INPUT -p tcp --dport 80 -j ACCEPT
|
|||
|
ip6tables -t filter -A INPUT -p tcp --dport 443 -j ACCEPT
|
|||
|
|
|||
|
# DNS In/Out
|
|||
|
ip6tables -t filter -A OUTPUT -p tcp --dport 53 -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p udp --dport 53 -j ACCEPT
|
|||
|
ip6tables -t filter -A INPUT -p tcp --dport 53 -j ACCEPT
|
|||
|
ip6tables -t filter -A INPUT -p udp --dport 53 -j ACCEPT
|
|||
|
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Droits</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -s
|
|||
|
chmod +x /usr/local/sbin/config_firewall
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Exécution script</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/usr/local/sbin/config_firewall
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vérifier l’établissement des règles IPV4 et IPV6</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -L
|
|||
|
ip6tables -L
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="sauvegarde-et-restauration-des-règles">Sauvegarde et restauration des règles</h3>
|
|||
|
|
|||
|
<p>Pour la sauvegarde et la restauration des règles, on installe le paquet <strong>iptables-persistent</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt-get install iptables-persistent
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Répondre au 2 questions pour sauvegarder les règles de base mises en place par le script précédent
|
|||
|
Faut-il enregistrer les règles IPv4 actuelles ? Oui<br>
|
|||
|
Faut-il enregistrer les règles IPv6 actuelles ? Oui</p>
|
|||
|
|
|||
|
<p>Vous pouvez utiliser <strong>dpkg-reconfigure</strong> pour exécuter cette étape ultérieurement.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo dpkg-reconfigure iptables-persistent
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Assurez-vous que Netfilter-persistent et actif au démarrage (netfilter-persistent.service enabled).</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl status netfilter-persistent
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>● netfilter-persistent.service - netfilter persistent configuration
|
|||
|
Loaded: loaded (/lib/systemd/system/netfilter-persistent.service; enabled; vendor preset: e
|
|||
|
nabled)
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Les règles de pare-feu IPv4/IPv6 ne sont pas sauvegardées automatiquement lors de l’arrêt du système.<br>
|
|||
|
Utilisez les commandes suivantes pour les mettre à jour.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables-save > /etc/iptables/rules.v4
|
|||
|
ip6tables-save > /etc/iptables/rules.v6
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p><strong>⚠ En cas de modification des règles iptables, ne pas oublier de faire les sauvegardes avant extinction de la machine</strong></p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<h2 id="enregistrement-log">Enregistrement (log)</h2>
|
|||
|
|
|||
|
<p>La cible LOG peut être utilisée pour enregistrer les paquets qui respectent une règle. Contrairement à d’autres cibles comme ACCEPT ou DROP, le paquet continuera à se déplacer dans la chaîne après avoir atteint une cible LOG. Cela signifie que pour permettre l’enregistrement de tous les paquets abandonnés, vous devez ajouter une règle LOG en double avant chaque règle DROP. Comme cela réduit l’efficacité et rend les choses moins simples, il est possible de créer une chaîne d’enregistrement à la place.</p>
|
|||
|
|
|||
|
<h3 id="créer-la-chaîne">Créer la chaîne</h3>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -N logdrop
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Et ajoutez les règles suivantes à la chaîne nouvellement créée :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -A logdrop -m limit --limit 5/m --limit-burst 10 -j LOG
|
|||
|
iptables -A logdrop -j DROP
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>L’explication des options de limite et d’éclatement limite est donnée ci-dessous.</p>
|
|||
|
|
|||
|
<p>Maintenant, chaque fois que nous voulons déposer un paquet et enregistrer cet événement, nous passons à la chaîne de dépôt, par exemple :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -A INPUT -m conntrack --ctstate INVALID -j logdrop
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="limitation-remplissage-log">Limitation remplissage log</h3>
|
|||
|
|
|||
|
<p>La chaîne de <code class="language-plaintext highlighter-rouge">logdrop</code> ci-dessus utilise le module limit pour empêcher que le log d’iptables ne devienne trop important ou ne provoque des écritures inutiles sur le disque dur. Sans limiter, un service configuré de façon erronée qui tente de se connecter, ou un attaquant, pourrait remplir le disque (ou au moins la partition /var) en provoquant des écritures dans le journal d’iptables.</p>
|
|||
|
|
|||
|
<p>Le module limit est appelé avec <code class="language-plaintext highlighter-rouge">-m limit</code>. Vous pouvez alors utiliser <code class="language-plaintext highlighter-rouge">--limit</code> pour définir un taux moyen et <code class="language-plaintext highlighter-rouge">--limit-burst</code> pour définir un taux de salves initial. Dans l’exemple de logdrop ci-dessus : <code class="language-plaintext highlighter-rouge">iptables -A logdrop -m limit --limit 5/m --limit-burst 10 -j LOG</code> ajoute une règle qui enregistre tous les paquets qui passent par elle. Les 10 premiers paquets consécutifs seront enregistrés, et à partir de ce moment, seuls 5 paquets par minute seront enregistrés. Le compte de “Limit Burst” est réinitialisé chaque fois que le “taux limite” n’est pas dépassé, c’est-à-dire que l’activité de journalisation revient automatiquement à la normale.</p>
|
|||
|
|
|||
|
<h3 id="enregistrer-tous-les-paquets-dentrée-supprimés-input-drop">Enregistrer tous les paquets d’entrée supprimés (INPUT DROP)</h3>
|
|||
|
|
|||
|
<p>Nous devons d’abord comprendre comment enregistrer tous les paquets d’entrée supprimés d’iptables dans syslog.</p>
|
|||
|
|
|||
|
<p>Si vous avez déjà tout un tas de règles de pare-feu iptables, ajoutez-les en bas, qui enregistrera tous les paquets d’entrée perdus (entrants) dans / var / log / messages</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -N LOGGING
|
|||
|
iptables -A INPUT -j LOGGING
|
|||
|
iptables -A LOGGING -m limit --limit 2/min -j LOG --log-prefix "IPTables-Dropped: " --log-level 4
|
|||
|
iptables -A LOGGING -j DROP
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Dans l’exemple ci-dessus, il effectue les opérations suivantes:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>
|
|||
|
<code class="language-plaintext highlighter-rouge">iptables -N LOGGING</code>: Créez une nouvelle chaîne appelée LOGGING</li>
|
|||
|
<li>
|
|||
|
<code class="language-plaintext highlighter-rouge">iptables -A INPUT -j LOGGING</code>: Tous les paquets entrants restants iront à la chaîne LOGGING</li>
|
|||
|
<li>
|
|||
|
<strong>ligne n° 3</strong>: connectez les paquets entrants à syslog (/ var / log / messages). Cette ligne est expliquée ci-dessous en détail.</li>
|
|||
|
<li>
|
|||
|
<code class="language-plaintext highlighter-rouge">iptables -A LOGGING -j DROP</code>: Enfin, supprimez tous les paquets qui sont arrivés à la chaîne LOGGING. c’est-à-dire que maintenant il supprime vraiment les paquets entrants.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Dans la ligne # 3 ci-dessus, il a les options suivantes pour enregistrer les paquets perdus:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>
|
|||
|
<code class="language-plaintext highlighter-rouge">-m limit</code>: Ceci utilise le module de correspondance de limite. En utilisant cela, vous pouvez limiter la journalisation en utilisant l’option –limit.</li>
|
|||
|
<li>
|
|||
|
<code class="language-plaintext highlighter-rouge">--limit 2/min</code>: indique le taux de correspondance moyen maximum pour la journalisation. Dans cet exemple, pour les paquets similaires, il limitera la journalisation à 2 par minute. Vous pouvez également spécifier 2 / seconde, 2 / minute, 2 / heure, 2 / jour. Cela est utile lorsque vous ne voulez pas encombrer vos messages de journal avec des messages répétés des mêmes paquets abandonnés.</li>
|
|||
|
<li>
|
|||
|
<code class="language-plaintext highlighter-rouge">-j LOG</code>: cela indique que la cible de ce paquet est LOG. c’est-à-dire écrire dans le fichier journal.</li>
|
|||
|
<li>
|
|||
|
<code class="language-plaintext highlighter-rouge">--log-prefix «IPTables-Dropped:»</code> Vous pouvez spécifier n’importe quel préfixe de journal, qui sera ajouté aux messages de journal qui seront écrits dans le fichier / var / log / messages</li>
|
|||
|
<li>
|
|||
|
<code class="language-plaintext highlighter-rouge">–-log-level 4</code> Il s’agit des niveaux syslog standard. 4 est un avertissement. Vous pouvez utiliser un nombre compris entre 0 et 7. 0 correspond à l’urgence et 7 au débogage.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h3 id="consigner-tous-les-paquets-sortants-supprimés-output-drop">Consigner tous les paquets sortants supprimés (OUTPUT DROP)</h3>
|
|||
|
|
|||
|
<p>C’est la même chose que ci-dessus, mais la 2e ligne ci-dessous a OUTPUT au lieu de INPUT.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -N LOGGING
|
|||
|
iptables -A OUTPUT -j LOGGING
|
|||
|
iptables -A LOGGING -m limit --limit 2/min -j LOG --log-prefix "IPTables-Dropped: " --log-level 4
|
|||
|
iptables -A LOGGING -j DROP
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="enregistrer-tous-les-paquets-supprimés-entrants-et-sortants-input-output-drop">Enregistrer tous les paquets supprimés (entrants et sortants INPUT OUTPUT DROP)</h3>
|
|||
|
|
|||
|
<p>C’est la même chose qu’avant, mais nous allons prendre la ligne numéro 2 des deux exemples précédents et l’ajouter ici. c’est-à-dire que nous aurons une ligne séparée pour INPUT et OUTPUT qui passera à la chaîne LOGGING.</p>
|
|||
|
|
|||
|
<p>Pour consigner les paquets perdus entrants et sortants, ajoutez les lignes suivantes au bas de vos règles de pare-feu iptables existantes.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -N LOGGING
|
|||
|
iptables -A INPUT -j LOGGING
|
|||
|
iptables -A OUTPUT -j LOGGING
|
|||
|
iptables -A LOGGING -m limit --limit 2/min -j LOG --log-prefix "IPTables-Dropped: " --log-level 4
|
|||
|
iptables -A LOGGING -j DROP
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>De plus, comme nous l’avons expliqué précédemment, par défaut, les iptables utiliseront /var/log/messages pour enregistrer tout le message. Si vous souhaitez le modifier dans votre propre fichier journal personnalisé, ajoutez la ligne suivante à /etc/syslog.conf</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kern.warning /var/log/custom.log
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="comment-lire-le-journal-iptables">Comment lire le journal IPTables</h3>
|
|||
|
|
|||
|
<p>Ce qui suit est un échantillon des lignes qui ont été enregistrées dans /var/log/messages lors de la suppression d’un paquet entrant et sortant.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Aug 4 13:22:40 centos kernel: IPTables-Dropped: IN= OUT=em1 SRC=192.168.1.23 DST=192.168.1.20 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=ICMP TYPE=8 CODE=0 ID=59228 SEQ=2
|
|||
|
Aug 4 13:23:00 centos kernel: IPTables-Dropped: IN=em1 OUT= MAC=a2:be:d2:ab:11:af:e2:f2:00:00 SRC=192.168.2.115 DST=192.168.1.23 LEN=52 TOS=0x00 PREC=0x00 TTL=127 ID=9434 DF PROTO=TCP SPT=58428 DPT=443 WINDOW=8192 RES=0x00 SYN URGP=0
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Dans la sortie ci-dessus:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>
|
|||
|
<strong>IPTables-Dropped</strong>: il s’agit du préfixe que nous avons utilisé dans notre journalisation en spécifiant l’option -log-prefix</li>
|
|||
|
<li>
|
|||
|
<strong>IN = em1</strong> Ceci indique l’interface qui a été utilisée pour ces paquets entrants. Ce sera vide pour les paquets sortants</li>
|
|||
|
<li>
|
|||
|
<strong>OUT = em1</strong> Ceci indique l’interface qui a été utilisée pour les paquets sortants. Ce sera vide pour les paquets entrants.</li>
|
|||
|
<li>
|
|||
|
<strong>SRC =</strong> l’adresse IP source d’où provient le paquet</li>
|
|||
|
<li>
|
|||
|
<strong>DST =</strong> l’adresse IP de destination à laquelle les paquets ont été envoyés</li>
|
|||
|
<li>
|
|||
|
<strong>LEN =</strong> Longueur du paquet</li>
|
|||
|
<li>
|
|||
|
<strong>PROTO =</strong> Indique le protocole (comme vous le voyez ci-dessus, la 1ère ligne est pour le protocole ICMP sortant, la 2ème ligne est pour le protocole TCP entrant)</li>
|
|||
|
<li>
|
|||
|
<strong>SPT =</strong> Indique le port source</li>
|
|||
|
<li>
|
|||
|
<strong>DPT =</strong> Indique le port de destination. Dans la 2ème ligne ci-dessus, le port de destination est 443. Cela indique que les paquets HTTPS entrants ont été abandonnés</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p><strong>Visualisation des paquets enregistrés (journalctl)</strong><br>
|
|||
|
Les paquets enregistrés sont visibles sous forme de messages du noyau dans le journal systemd.<br>
|
|||
|
Pour voir tous les paquets qui ont été enregistrés depuis le dernier démarrage de la machine :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>journalctl -k | grep "IN=.*OUT=.*" | less
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h2 id="tutoriel---configurer-un-pare-feu-avec-iptables">Tutoriel - Configurer un pare-feu avec Iptables</h2>
|
|||
|
|
|||
|
<p><strong>COPIE de l’article publié par <a href="https://www.microlinux.fr/author/kikinovak/">kikinovak</a> le 1 février 2019</strong></p>
|
|||
|
|
|||
|
<p>Cet article décrit pas à pas la configuration d’un pare-feu sur un système Linux. Il s’agit là d’une introduction détaillée sous forme d’atelier pratique, et qui s’adresse aux administrateurs en herbe aussi bien qu’aux experts qui souhaitent réviser leurs bases. Tous les exemples de cette présentation ont été mis en pratique sur une machine tournant sous CentOS 7, et j’ai fait de mon mieux pour m’affranchir des spécificités de cette distribution.</p>
|
|||
|
|
|||
|
<h3 id="généralités">Généralités</h3>
|
|||
|
|
|||
|
<p>Le terme de « pare-feu » (ou firewall) peut désigner plusieurs choses.</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Tout type de matériel qui agit comme une passerelle entre Internet et le réseau local.</li>
|
|||
|
<li>Une application installée sur la machine et censée améliorer la sécurité.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>En ce qui nous concerne, il s’agira très précisément d’un filtre de paquets pour la sécurisation du trafic TCP/IP. Ce filtre devra analyser tous les paquets réseau qui entrent dans la machine et qui la quittent. Une série de tests décidera si les paquets ont le droit de passer ou s’ils doivent être bloqués.</p>
|
|||
|
|
|||
|
<h3 id="netfilter">Netfilter</h3>
|
|||
|
|
|||
|
<p>Le système de gestion de paquets réseau du noyau Linux s’appelle Netfilter. La commande utilisée pour le configurer est iptables.</p>
|
|||
|
|
|||
|
<p>Voici un schéma simplifié du cheminement d’un paquet TCP/IP dans le kernel.</p>
|
|||
|
|
|||
|
<p><img src="/images/netfilter.png" alt="Texte alternatif"></p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Traditionnellement, les schémas de pare-feu montrent l’Internet « dangereux » à gauche, puis le pare-feu, et enfin à droite le réseau local « sécurisé ». Ce n’est pas le cas dans ce schéma, où les paquets entrant dans la machine depuis la gauche peuvent provenir aussi bien d’Internet que du réseau local. Il en est de même pour les paquets qui sortent du pare-feu à droite.</li>
|
|||
|
<li>
|
|||
|
<em>Routing</em> : En fonction de l’adresse IP et du numéro de port du paquet, le kernel décide si le paquet doit être traité localement ou s’il doit être transmis vers une interface réseau et donc vers une autre machine du réseau local ou même de l’Internet.</li>
|
|||
|
<li>
|
|||
|
<em>Filter Input</em> : Une série de tests basés sur un certain nombre de règles décident si le paquet est accepté pour être traité par des applications locales ou non.</li>
|
|||
|
<li>
|
|||
|
<em>Local Process</em> : Il s’agit là tout simplement de l’ensemble des applications qui traitent – ou qui produisent – des paquets IP sur la machine locale, c’est-à-dire tous les services réseau : sshd, httpd, etc.</li>
|
|||
|
<li>
|
|||
|
<em>Filter Output</em> : Une autre série de tests basés sur une autre série de règles établit si le paquet a le droit de quitter le kernel.</li>
|
|||
|
<li>
|
|||
|
<em>Filter Forward</em> : Ce filtre effectue des tests sur les paquets qui sont transmis sans être traités et décide s’ils ont le droit de continuer leur chemin.</li>
|
|||
|
<li>
|
|||
|
<em>NAT Postrouting</em> : Au cas où la machine locale assure la connexion Internet pour d’autres machines grâce au relais de paquets (Masquerading), cette étape gère la manipulation nécessaire des paquets.</li>
|
|||
|
<li>Le filtre de paquets gèrera les étapes <em>Filter Input, Filter Output, Filter Forward</em> et, le cas échéant, <em>NAT Postrouting</em>.</li>
|
|||
|
<li>Les parties <em>Routing</em> et <em>Local Process</em> du schéma concernent les fonctions réseau du kernel ou des services réseau communs et n’ont rien à voir avec le filtre de paquets.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h3 id="les-différentes-actions">Les différentes actions</h3>
|
|||
|
|
|||
|
<p>C’est le kernel qui gère la transmission des paquets provenant d’une interface réseau ou générés par une application locale. À chacune des étapes du système de filtrage, il a respectivement trois alternatives.</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>
|
|||
|
<strong>DENY</strong> : la transmission du paquet est tout bonnement refusée, sans message d’erreur. Le paquet tombe à la trappe, dans le nirvana numérique. Il n’existe plus.</li>
|
|||
|
<li>
|
|||
|
<strong>REJECT</strong> : la transmission du paquet est refusée, avec un message d’erreur. Les conséquences pour le paquet sont les mêmes que pour un DENY. La différence, c’est que le destinataire est informé par un paquet ICMP (Internet Control Message Protocol) du fait que son paquet a été refusé.</li>
|
|||
|
<li>
|
|||
|
<strong>ACCEPT</strong> : le paquet est transmis.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h3 id="les-tables">Les tables</h3>
|
|||
|
|
|||
|
<p>L’idée de base d’un système Netfilter, c’est qu’un paquet IP traverse différents endroits dans le kernel, qui testent à partir d’une série de règles si le paquet est autorisé ou non. Si c’est le cas, le paquet est transmis. Si ce n’est pas le cas, le paquet est supprimé ou renvoyé au destinataire. Trois tables contrôlent le filtre Netfilter.</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>La table <strong>filter</strong> : cette table contient généralement l’ensemble des règles pour le filtre de paquets à proprement parler.</li>
|
|||
|
<li>La table <strong>nat</strong> : cette table est active uniquement si la fonction de relais des paquets du kernel (IP Masquerading) a été activée. Elle permet de modifier l’adresse des paquets qui entrent dans le kernel depuis l’extérieur ou alors qui en sortent à nouveau (Network Address Translation).</li>
|
|||
|
<li>La table <strong>mangle</strong> : elle permet de procéder à diverses manipulations des paquets IP. Cette table est réservée à une série d’opérations très spécifiques, et nous ne la traiterons pas ici.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h3 id="les-chaînes">Les chaînes</h3>
|
|||
|
|
|||
|
<p>Chacune de ces trois tables prévoit à son tour une série de chaînes de règles.</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Table <strong>filter</strong> : INPUT, FORWARD et OUTPUT</li>
|
|||
|
<li>Table <strong>nat</strong> : PREROUTING, INPUT, OUTPUT et POSTROUTING</li>
|
|||
|
<li>Table <strong>mangle</strong> : PREROUTING, INPUT, FORWARD, OUTPUT et POSTROUTING</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Ces chaînes de règles sont indépendantes les unes des autres. Il existe donc bien trois chaînes INPUT, deux chaînes FORWARD, deux chaînes PREROUTING, deux chaînes POSTROUTING et trois chaînes OUTPUT.</p>
|
|||
|
|
|||
|
<p>Lorsqu’une documentation se réfère à « la chaîne OUTPUT » sans plus de précisions de la table à laquelle appartient la chaîne, il s’agit dans tous les cas de la table filter, qui est de loin la plus importante.</p>
|
|||
|
|
|||
|
<p>La même chose vaut d’ailleurs pour la commande <code class="language-plaintext highlighter-rouge">iptables</code>. L’option -t permet d’indiquer la table pour laquelle on souhaite définir des règles. Si l’on omet cette option, c’est automatiquement la table filter qui est sélectionnée.</p>
|
|||
|
|
|||
|
<h3 id="fonctionnement-de-base">Fonctionnement de base</h3>
|
|||
|
|
|||
|
<p>Lorsqu’un paquet IP rencontre une chaîne de règles dans son cheminement à travers le kernel, celui-ci vérifie les règles en question l’une après l’autre.</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Dès qu’une règle s’applique à un paquet, l’action prévue dans la règle est effectuée : transmettre le paquet, le supprimer ou le renvoyer au destinataire.</li>
|
|||
|
<li>Lorsqu’aucune des règles ne peut s’appliquer pour le paquet, c’est la politique par défaut qui entre en vigueur. Là encore, on peut se retrouver avec les trois cas de figure : transmettre, supprimer, rejeter.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>La configuration d’un pare-feu consiste donc à définir la politique par défaut ainsi qu’une série de règles pour chacune des chaînes de filtres essentielles.</p>
|
|||
|
|
|||
|
<h3 id="avant-de-mettre-la-main-à-la-pâte">Avant de mettre la main à la pâte</h3>
|
|||
|
|
|||
|
<p>Tous les exemples qui suivent ont été mis en pratique sur une machine tournant sous CentOS 7. Depuis la version 7, CentOS utilise <code class="language-plaintext highlighter-rouge">firewalld</code>, une couche d’abstraction supplémentaire à la sauce Red Hat, dont on peut se demander à quoi elle peut bien servir, vu qu’elle utilise <code class="language-plaintext highlighter-rouge">iptables</code> sous le capot. Quoi qu’il en soit, pour éviter que <code class="language-plaintext highlighter-rouge">firewalld</code> ne nous tire dans les pattes, nous allons désactiver le service et supprimer le paquet correspondant.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo systemctl stop firewalld
|
|||
|
$ sudo yum remove firewalld
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour nos travaux pratiques, nous aurons besoin du paquet iptables, qui fait normalement partie d’une installation minimale de CentOS.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ rpm -q iptables
|
|||
|
iptables-1.4.21-28.el7.x86_64
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Veillez à ce que le paquet <em>iptables-services</em> ne soit pas présent sur le système. Il s’agit là d’une autre idiosyncrasie de Red Hat, que nous utiliserons par la suite pour une configuration persistante. Pour l’instant, ne l’installez pas.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ rpm -q iptables-services
|
|||
|
package iptables-services is not installed
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Enfin, je me suis servi de la <strong>machine physique</strong> <amandine.microlinux.lan> dans mon réseau pour tous mes tests. Avec une machine virtuelle, on risque tôt ou tard de se tirer dans le pied lorsqu’on s’amuse à fermer toutes les écoutilles et qu’elle devient dure de la feuille à tel point qu’il faut l’éteindre à coups de pieds. Pour simplifier les choses, j’ai également désactivé le protocole IPv6 sur cette machine.</amandine.microlinux.lan></p>
|
|||
|
|
|||
|
<h3 id="afficher-létat-du-pare-feu">Afficher l’état du pare-feu</h3>
|
|||
|
|
|||
|
<p>L’option <code class="language-plaintext highlighter-rouge">-L</code> (ou <code class="language-plaintext highlighter-rouge">--list</code>) permet d’afficher l’état du pare-feu. Sans autre option, elle affiche les règles de la table filter.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -L
|
|||
|
Chain INPUT (policy ACCEPT)
|
|||
|
target prot opt source destination
|
|||
|
|
|||
|
Chain FORWARD (policy ACCEPT)
|
|||
|
target prot opt source destination
|
|||
|
|
|||
|
Chain OUTPUT (policy ACCEPT)
|
|||
|
target prot opt source destination
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>L’option <code class="language-plaintext highlighter-rouge">-t</code> (ou <code class="language-plaintext highlighter-rouge">--table</code>) permet de sélectionner la table. Par défaut, c’est la table filter qui est affichée. La commande précédente est donc identique à celle-ci.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -t filter -L
|
|||
|
...
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Les chaînes des tables <strong>nat</strong> et <strong>mangle</strong> sont affichées comme ceci.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -t nat -L
|
|||
|
...
|
|||
|
$ sudo iptables -t mangle -L
|
|||
|
...
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour afficher seulement les règles d’une certaine chaîne, on peut la fournir en argument.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -L INPUT
|
|||
|
Chain INPUT (policy ACCEPT)
|
|||
|
target prot opt source destination
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ou encore :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -L OUTPUT
|
|||
|
Chain OUTPUT (policy ACCEPT)
|
|||
|
target prot opt source destination
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ou encore :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -t nat -L POSTROUTING
|
|||
|
Chain POSTROUTING (policy ACCEPT)
|
|||
|
target prot opt source destination
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Dans la vie courante, on utilise souvent la combinaison d’options suivante pour afficher l’état du pare-feu.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -L -vn
|
|||
|
Chain INPUT (policy ACCEPT 568 packets, 39983 bytes)
|
|||
|
pkts bytes target prot opt in out source destination
|
|||
|
|
|||
|
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
|
|||
|
pkts bytes target prot opt in out source destination
|
|||
|
|
|||
|
Chain OUTPUT (policy ACCEPT 293 packets, 30992 bytes)
|
|||
|
pkts bytes target prot opt in out source destination
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>L’option <code class="language-plaintext highlighter-rouge">-v</code> (ou <code class="language-plaintext highlighter-rouge">--verbose</code>) permet d’afficher plus de détails.</li>
|
|||
|
<li>L’option <code class="language-plaintext highlighter-rouge">-n</code> (ou <code class="language-plaintext highlighter-rouge">--numeric</code>) peut s’avérer pratique dans la mesure où elle permet un formatage numérique de l’affichage pour les adresses IP et les ports.</li>
|
|||
|
<li>Dans certains cas, on ajoutera l’option <code class="language-plaintext highlighter-rouge">--line-numbers</code> pour ajouter des numéros de ligne au début de chaque règle, ce qui permet d’identifier sa position dans la chaîne.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Pour afficher la table <strong>nat</strong> avec des numéros de ligne, on invoquera donc la commande suivante.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -t nat -L -vn --line-numbers
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="définition-manuelle-des-premières-règles">Définition manuelle des premières règles</h3>
|
|||
|
|
|||
|
<p>L’option <code class="language-plaintext highlighter-rouge">-P</code> (ou <code class="language-plaintext highlighter-rouge">--policy</code>) permet de définir la politique par défaut. Pour commencer, nous allons bloquer les connexions entrantes.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -P INPUT DROP
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Notre machine devient injoignable depuis l’extérieur.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[kikinovak@alphamule:~] $ ping amandine
|
|||
|
[kikinovak@alphamule:~] $ ssh amandine
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Non content de cela, elle devient également injoignable pour elle-même. Et si jamais vous avez effectué vos tests sur une machine virtuelle en faisant fi de ma mise en garde initiale, c’est là le moment précis où vous vous êtes tiré dans le pied.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[kikinovak@amandine:~] $ ping localhost
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>On va donc autoriser les paquets entrants sur la boucle locale.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -A INPUT -i lo -j ACCEPT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>L’option <code class="language-plaintext highlighter-rouge">-A</code> (ou <code class="language-plaintext highlighter-rouge">--append</code>) permet d’ajouter une règle à la fin de la chaîne sélectionnée.</li>
|
|||
|
<li>L’option <code class="language-plaintext highlighter-rouge">-i</code> (ou <code class="language-plaintext highlighter-rouge">--in-interface</code>) permet de spécifier l’interface réseau par laquelle un paquet a été reçu.</li>
|
|||
|
<li>L’option <code class="language-plaintext highlighter-rouge">-j</code> (ou <code class="language-plaintext highlighter-rouge">--jump</code>) spécifie la cible de règle, autrement dit, elle indique ce qu’il faut faire si le paquet correspond à la règle.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>On obtient donc ceci.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -L INPUT -vn
|
|||
|
Chain INPUT (policy DROP 10 packets, 1184 bytes)
|
|||
|
pkts bytes target prot opt in out source destination
|
|||
|
4 336 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Et la machine peut à nouveau se causer à elle-même.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[kikinovak@amandine:~] $ ping localhost
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>On peut décider d’autoriser les pings en provenance de l’extérieur. Pour ce faire, on pourrait par exemple autoriser le protocole ICMP en utilisant l’option <code class="language-plaintext highlighter-rouge">-p</code> (comme <code class="language-plaintext highlighter-rouge">--protocol</code>).</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -A INPUT -p icmp -j ACCEPT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Nous sommes peut-être allés un peu trop loin dans la définition de la règle précédente. En effet, le protocole ICMP comprend toute une série de types de paquets. La commande suivante permet de les afficher.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -p icmp -h | less
|
|||
|
...
|
|||
|
Valid ICMP Types:
|
|||
|
any
|
|||
|
echo-reply (pong)
|
|||
|
destination-unreachable
|
|||
|
network-unreachable
|
|||
|
host-unreachable
|
|||
|
protocol-unreachable
|
|||
|
port-unreachable
|
|||
|
fragmentation-needed
|
|||
|
source-route-failed
|
|||
|
network-unknown
|
|||
|
host-unknown
|
|||
|
network-prohibited
|
|||
|
host-prohibited
|
|||
|
TOS-network-unreachable
|
|||
|
TOS-host-unreachable
|
|||
|
communication-prohibited
|
|||
|
host-precedence-violation
|
|||
|
precedence-cutoff
|
|||
|
source-quench
|
|||
|
redirect
|
|||
|
network-redirect
|
|||
|
host-redirect
|
|||
|
TOS-network-redirect
|
|||
|
TOS-host-redirect
|
|||
|
echo-request (ping)
|
|||
|
router-advertisement
|
|||
|
router-solicitation
|
|||
|
time-exceeded (ttl-exceeded)
|
|||
|
ttl-zero-during-transit
|
|||
|
ttl-zero-during-reassembly
|
|||
|
parameter-problem
|
|||
|
ip-header-bad
|
|||
|
required-option-missing
|
|||
|
timestamp-request
|
|||
|
timestamp-reply
|
|||
|
address-mask-request
|
|||
|
address-mask-reply
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Au lieu d’accepter tout ce fatras « à la louche », nous allons uniquement autoriser ce qu’il faut pour que la machine soit « pingable ». Avant d’aller plus loin, je vais d’abord supprimer la règle que je viens de définir.</p>
|
|||
|
|
|||
|
<p>Dans un premier temps, j’affiche les règles avec l’option <code class="language-plaintext highlighter-rouge">--line-numbers</code>.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -L --line-numbers
|
|||
|
Chain INPUT (policy DROP)
|
|||
|
num target prot opt source destination
|
|||
|
1 ACCEPT all -- anywhere anywhere
|
|||
|
2 ACCEPT icmp -- anywhere anywhere
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ensuite, je supprime la règle n° 2 de la chaine INPUT en utilisant l’option <code class="language-plaintext highlighter-rouge">-D</code> (comme <code class="language-plaintext highlighter-rouge">--delete</code>).</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -D INPUT 2
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Il ne me reste qu’à autoriser les trois types de paquets nécessaires pour que le ping fonctionne correctement.</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>echo-request</li>
|
|||
|
<li>time-exceeded</li>
|
|||
|
<li>destination-unreachable</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Concrètement, les commandes pour autoriser ces trois types de paquets ressembleront à ceci.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
|
|||
|
$ sudo iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT
|
|||
|
$ sudo iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="retour-à-la-case-départ">Retour à la case départ</h3>
|
|||
|
|
|||
|
<p>Avant d’aller plus loin dans la définition des règles de filtrage, nous allons voir comment arrêter le pare-feu. À l’état brut tel que nous l’utilisons, iptables n’est pas un démon qu’on peut démarrer et arrêter. C’est donc un peu plus compliqué d’arrêter le filtrage des paquets dans l’état actuel des choses.</p>
|
|||
|
|
|||
|
<p>Comme nous l’avons vu un peu plus haut, l’option <code class="language-plaintext highlighter-rouge">-P</code> (ou <code class="language-plaintext highlighter-rouge">--policy</code>) se charge de définir la politique par défaut. Puisque nous souhaitons arrêter le pare-feu, cela équivaut à définir partout une politique par défaut <strong>ACCEPT</strong>.</p>
|
|||
|
|
|||
|
<p>Sur toutes les chaînes de la table <strong>filter</strong> :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -P INPUT ACCEPT
|
|||
|
$ sudo iptables -P FORWARD ACCEPT
|
|||
|
$ sudo iptables -P OUTPUT ACCEPT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Sur toutes les chaînes de la table <strong>nat</strong> :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -t nat -P PREROUTING ACCEPT
|
|||
|
$ sudo iptables -t nat -P INPUT ACCEPT
|
|||
|
$ sudo iptables -t nat -P OUTPUT ACCEPT
|
|||
|
$ sudo iptables -t nat -P POSTROUTING ACCEPT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Et enfin sur toutes les chaînes de la table <strong>mangle</strong> :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -t mangle -P PREROUTING ACCEPT
|
|||
|
$ sudo iptables -t mangle -P INPUT ACCEPT
|
|||
|
$ sudo iptables -t mangle -P FORWARD ACCEPT
|
|||
|
$ sudo iptables -t mangle -P OUTPUT ACCEPT
|
|||
|
$ sudo iptables -t mangle -P POSTROUTING ACCEPT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ensuite, il faut remettre à zéro tous les compteurs de paquets et d’octets dans toutes les chaînes. Pour ce faire, on utilise l’option <code class="language-plaintext highlighter-rouge">-Z</code> (ou <code class="language-plaintext highlighter-rouge">--zero</code>).</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -t filter -Z
|
|||
|
$ sudo iptables -t nat -Z
|
|||
|
$ sudo iptables -t mangle -Z
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Il ne reste plus qu’à supprimer toutes les règles et toutes les chaînes.</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>L’option <code class="language-plaintext highlighter-rouge">-F</code> (ou <code class="language-plaintext highlighter-rouge">--flush</code>) se charge de supprimer les chaînes d’une table.</li>
|
|||
|
<li>L’option <code class="language-plaintext highlighter-rouge">-X</code> (ou <code class="language-plaintext highlighter-rouge">--delete-chain</code>) supprime les chaînes personnalisées définies par l’utilisateur.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Pour supprimer tous les jeux de règles sur toutes les tables, on invoquera donc la série de commandes suivante.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -t filter -F
|
|||
|
$ sudo iptables -t filter -X
|
|||
|
$ sudo iptables -t nat -F
|
|||
|
$ sudo iptables -t nat -X
|
|||
|
$ sudo iptables -t mangle -F
|
|||
|
$ sudo iptables -t mangle -X
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="scripter-les-règles-de-filtrage">Scripter les règles de filtrage</h3>
|
|||
|
|
|||
|
<p>Vous trouvez sans doute que toutes ces règles de filtrage sont fastidieuses à taper. Pour cette raison, nous allons ranger tout cela soigneusement dans un script avant d’aller plus loin.</p>
|
|||
|
|
|||
|
<p>Éditez un script <strong>firewall.sh</strong> (avec les droits de <strong>root</strong>, bien sûr), rangez-le dans un endroit approprié comme <em>/usr/local/sbin</em> et définissez les permissions qui vont bien (rwx——).</p>
|
|||
|
|
|||
|
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
|
|||
|
<span class="c">#</span>
|
|||
|
<span class="c"># firewall.sh</span>
|
|||
|
|
|||
|
<span class="nv">IPT</span><span class="o">=</span>/usr/sbin/iptables
|
|||
|
|
|||
|
<span class="c"># Tout accepter</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-P</span> INPUT ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-P</span> FORWARD ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-P</span> OUTPUT ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-P</span> PREROUTING ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-P</span> POSTROUTING ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-P</span> OUTPUT ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-P</span> PREROUTING ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-P</span> INPUT ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-P</span> FORWARD ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-P</span> OUTPUT ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-P</span> POSTROUTING ACCEPT
|
|||
|
|
|||
|
<span class="c"># Remettre les compteurs à zéro</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-Z</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-Z</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-Z</span>
|
|||
|
|
|||
|
<span class="c"># Supprimer toutes les règles actives et les chaînes personnalisées</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-F</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-X</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-F</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-X</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-F</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-X</span>
|
|||
|
|
|||
|
<span class="c"># Politique par défaut</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-P</span> INPUT DROP
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-P</span> FORWARD DROP
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-P</span> OUTPUT ACCEPT
|
|||
|
|
|||
|
<span class="c"># Faire confiance à nous-mêmes ;o)</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-i</span> lo <span class="nt">-j</span> ACCEPT
|
|||
|
|
|||
|
<span class="c"># Ping</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> icmp <span class="nt">--icmp-type</span> echo-request <span class="nt">-j</span> ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> icmp <span class="nt">--icmp-type</span> time-exceeded <span class="nt">-j</span> ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> icmp <span class="nt">--icmp-type</span> destination-unreachable <span class="nt">-j</span> ACCEPT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Dans un premier temps, le script prend soin de supprimer toutes les règles existantes.</li>
|
|||
|
<li>Notez que la politique par défaut consiste à rejeter (<strong>DROP</strong>) les paquets entrants (<strong>INPUT</strong>) et relayés (<strong>FORWARD</strong>) et à accepter (<strong>ACCEPT</strong>) les paquets sortants.</li>
|
|||
|
<li>Pour l’instant, la machine ne peut pas faire grand-chose, si ce n’est se connecter à elle-même et accepter le <code class="language-plaintext highlighter-rouge">ping</code> depuis l’extérieur.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Lancez le script.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo /usr/local/sbin/firewall.sh
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Affichez les règles du pare-feu.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -L -vn
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vérifiez depuis une machine extérieure si la machine se comporte bien comme prévu. Normalement, vous devez pouvoir envoyer un <code class="language-plaintext highlighter-rouge">ping</code>. En revanche, il ne vous sera pas possible de vous connecter via SSH.</p>
|
|||
|
|
|||
|
<p>À présent, redémarrez la machine et affichez à nouveau les règles du pare-feu. Vous constatez qu’elles ne sont plus en vigueur.</p>
|
|||
|
|
|||
|
<h3 id="configurer-un-pare-feu-persistant">Configurer un pare-feu persistant</h3>
|
|||
|
|
|||
|
<p>La prochaine étape consistera à rendre les règles de filtrage persistantes. Le moyen le plus simple et le plus sûr consiste ici à utiliser le paquet <strong>iptables-services</strong>, qui est spécifique aux distributions de la famille Red Hat.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo yum install iptables-services
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Activez et démarrez le service correspondant.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo systemctl enable iptables
|
|||
|
$ sudo systemctl start iptables
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>À présent, si vous affichez les règles du pare-feu (<code class="language-plaintext highlighter-rouge">sudo iptables -L -vn</code>), vous serez sans doute surpris de voir apparaître un jeu de règles inédit, et vous vous demanderez probablement d’où il peut bien sortir.</p>
|
|||
|
|
|||
|
<p>En effet, le paquet iptables-services installe son propre jeu de règles par défaut, défini dans le fichier <strong>/etc/sysconfig/iptables</strong>, et dont la syntaxe rappelle vaguement ce que nous avons vu un peu plus haut.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo cat /etc/sysconfig/iptables
|
|||
|
...
|
|||
|
*filter
|
|||
|
:INPUT ACCEPT [0:0]
|
|||
|
:FORWARD ACCEPT [0:0]
|
|||
|
:OUTPUT ACCEPT [0:0]
|
|||
|
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
|
|||
|
-A INPUT -p icmp -j ACCEPT
|
|||
|
-A INPUT -i lo -j ACCEPT
|
|||
|
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
|
|||
|
-A INPUT -j REJECT --reject-with icmp-host-prohibited
|
|||
|
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
|
|||
|
COMMIT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour remplacer ce nouveau jeu de règles de filtrage par notre propre panoplie de règles, il suffit de relancer le script que nous avons édité tout à l’heure.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo /usr/local/sbin/firewall.sh
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Affichez les règles de filtrage.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -L -vn
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour enregistrer nos règles personnalisées, utilisez la commande suivante.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo service iptables save
|
|||
|
iptables: Saving firewall rules to /etc/sysconfig/iptables: [OK]
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Éventuellement, jetez un oeil dans <strong>/etc/sysconfig/iptables</strong>. Vous y reconnaîtrez notre jeu de règles traduit dans la syntaxe propre à Red Hat. Cette fois-ci, la configuration du pare-feu persiste même après un redémarrage de la machine.</p>
|
|||
|
|
|||
|
<h3 id="ajouter-quelques-règles-de-base">Ajouter quelques règles de base</h3>
|
|||
|
|
|||
|
<p>Notre pare-feu est désormais plus confortable à manier. Nous n’avons plus à taper une myriade de commandes pour le démarrer ou pour l’arrêter, et la configuration persiste après un redémarrage. Partant de là, nous allons ajouter une poignée de règles de filtrage pour disposer d’un pare-feu opérationnel.</p>
|
|||
|
|
|||
|
<p>La règle suivante autorise les paquets provenant de connexions déjà établies.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ensuite, nous allons ouvrir le port 22 pour pouvoir nous connecter en SSH. Notez qu’ici nous précisons non seulement le protocole TCP avec l’option <code class="language-plaintext highlighter-rouge">-p</code> (comme <code class="language-plaintext highlighter-rouge">--protocol</code>), mais aussi l’interface réseau <strong>enp2s0</strong> avec l’option <code class="language-plaintext highlighter-rouge">-i</code> (comme <code class="language-plaintext highlighter-rouge">--in-interface</code>).</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -A INPUT -p tcp -i enp2s0 --dport 22 -j ACCEPT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Si nous intégrons tout cela à notre script, voici ce que nous obtenons au total.</p>
|
|||
|
|
|||
|
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
|
|||
|
<span class="c">#</span>
|
|||
|
<span class="c"># firewall.sh</span>
|
|||
|
|
|||
|
<span class="nv">IPT</span><span class="o">=</span>/usr/sbin/iptables
|
|||
|
<span class="nv">SERVICE</span><span class="o">=</span>/usr/sbin/service
|
|||
|
|
|||
|
<span class="c"># Réseau local</span>
|
|||
|
<span class="nv">IFACE_LAN</span><span class="o">=</span>enp2s0
|
|||
|
|
|||
|
<span class="c"># Tout accepter</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-P</span> INPUT ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-P</span> FORWARD ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-P</span> OUTPUT ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-P</span> PREROUTING ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-P</span> POSTROUTING ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-P</span> OUTPUT ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-P</span> PREROUTING ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-P</span> INPUT ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-P</span> FORWARD ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-P</span> OUTPUT ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-P</span> POSTROUTING ACCEPT
|
|||
|
|
|||
|
<span class="c"># Remettre les compteurs à zéro</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-Z</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-Z</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-Z</span>
|
|||
|
|
|||
|
<span class="c"># Supprimer toutes les règles actives et les chaînes personnalisées</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-F</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-X</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-F</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-X</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-F</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-X</span>
|
|||
|
|
|||
|
<span class="c"># Politique par défaut</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-P</span> INPUT DROP
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-P</span> FORWARD DROP
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-P</span> OUTPUT ACCEPT
|
|||
|
|
|||
|
<span class="c"># Faire confiance à nous-mêmes ;o)</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-i</span> lo <span class="nt">-j</span> ACCEPT
|
|||
|
|
|||
|
<span class="c"># Ping</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> icmp <span class="nt">--icmp-type</span> echo-request <span class="nt">-j</span> ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> icmp <span class="nt">--icmp-type</span> time-exceeded <span class="nt">-j</span> ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> icmp <span class="nt">--icmp-type</span> destination-unreachable <span class="nt">-j</span> ACCEPT
|
|||
|
|
|||
|
<span class="c"># Connexions établies</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-m</span> state <span class="nt">--state</span> ESTABLISHED <span class="nt">-j</span> ACCEPT
|
|||
|
|
|||
|
<span class="c"># SSH </span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> tcp <span class="nt">-i</span> <span class="nv">$IFACE_LAN</span> <span class="nt">--dport</span> 22 <span class="nt">-j</span> ACCEPT
|
|||
|
|
|||
|
<span class="c"># Enregistrer la configuration</span>
|
|||
|
<span class="nv">$SERVICE</span> iptables save
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Dans l’état actuel, notre pare-feu est déjà utilisable sur une machine <em>standalone</em>, c’est-à-dire qui ne fait pas office de passerelle entre deux réseaux.</p>
|
|||
|
|
|||
|
<h3 id="tester-le-pare-feu">Tester le pare-feu</h3>
|
|||
|
|
|||
|
<p>Le moment est venu de soumettre notre pare-feu à quelques tests. Installez <strong>nmap</strong>, un excellent outil qui permet de voir à quoi ressemble notre pare-feu depuis une machine extérieure, et lancez-le.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[root@alphamule:~] $ sudo nmap amandine
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-01 09:29 CET
|
|||
|
Nmap scan report for amandine (192.168.2.5)
|
|||
|
Host is up (0.00018s latency).
|
|||
|
rDNS record for 192.168.2.5: amandine.microlinux.lan
|
|||
|
Not shown: 999 filtered ports
|
|||
|
PORT STATE SERVICE
|
|||
|
22/tcp open ssh
|
|||
|
MAC Address: 00:19:BB:E3:31:CE (Hewlett Packard)
|
|||
|
Nmap done: 1 IP address (1 host up) scanned in 4.70 seconds
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Sur le pare-feu, <code class="language-plaintext highlighter-rouge">netstat</code> permet de voir toutes les sockets réseau ouvertes et à l’écoute de nouvelles connexions.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[root@amandine:~] $ sudo netstat -untap
|
|||
|
Active Internet connections (servers and established)
|
|||
|
... Local Address Foreign Address State PID/Program name
|
|||
|
... 0 0.0.0.0:22 0.0.0.0:* LISTEN 831/sshd
|
|||
|
... 192.168.2.5:22 192.168.2.2:41304 ESTABLISHED 1419/sshd: root@pts
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>L’outil <code class="language-plaintext highlighter-rouge">netstat</code> vous permettra plus généralement de traquer et désactiver les services inutiles.</p>
|
|||
|
|
|||
|
<h3 id="enregistrer-les-paquets-refusés">Enregistrer les paquets refusés</h3>
|
|||
|
|
|||
|
<p>Au stade actuel, ce n’est peut-être pas une mauvaise idée de garder une trace de tous les paquets refusés par notre pare-feu. Ajoutez la règle suivante vers la fin du script, après toutes les autres règles de filtrage.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -A INPUT -j LOG --log-prefix "++ IPv4 packet rejected ++ "
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Relancez le script pour prendre en compte les modifications.</p>
|
|||
|
|
|||
|
<p>Un moyen très simple pour effectuer un test, c’est d’installer un serveur Apache (<code class="language-plaintext highlighter-rouge">sudo yum install httpd</code>) et de le lancer (<code class="language-plaintext highlighter-rouge">sudo systemctl start httpd</code>). Ensuite, on ouvre un navigateur Web sur une machine du réseau local, et on tente de se connecter au serveur. Comme il faut s’y attendre, la page par défaut ne s’affiche pas. Jetons un oeil dans les logs.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo tail -f /var/log/messages
|
|||
|
Feb 1 09:43:50 amandine kernel: +++ IPv4 packet rejected +++
|
|||
|
IN=enp63s0 OUT= MAC=00:19:bb:e3:31:ce:d4:85:64:b2:b2:1b:08:00
|
|||
|
SRC=192.168.2.2 DST=192.168.2.5 LEN=60 TOS=0x00 PREC=0x00 TTL=64
|
|||
|
ID=53498 DF PROTO=TCP SPT=59416 DPT=80 WINDOW=29200 RES=0x00 SYN
|
|||
|
URGP=0
|
|||
|
...
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Nous voyons que la machine <strong>192.168.2.2</strong> dans le réseau local a tenté vainement d’initier une connexion sur le port 80 du serveur.</p>
|
|||
|
|
|||
|
<p>L’enregistrement des paquets peut être amélioré comme ceci.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Enregistrer les connexions refusées
|
|||
|
$IPT -A INPUT -m limit --limit 2/min -j LOG \
|
|||
|
--log-prefix "++ IPv4 packet rejected ++ "
|
|||
|
$IPT -A INPUT -j DROP
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Le souci avec la journalisation des paquets refusés, c’est qu’on risque de se faire submerger les logs par les tentatives de connexion quelque peu insistantes. On va donc limiter la journalisation à deux enregistrements par minute pour un même type de paquet.</li>
|
|||
|
<li>La deuxième ligne sert à clôturer la configuration. À partir de là, les paquets entrants sont définitivement refusés.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h3 id="autoriser-les-services-de-base">Autoriser les services de base</h3>
|
|||
|
|
|||
|
<p>Nous disposons à présent d’un pare-feu minimal fonctionnel qui autorise les connexions locales en SSH, en bloquant tout le reste et en gardant une trace des connexions refusées. À partir de là, notre politique consistera à autoriser juste les services nécessaires, en les ajoutant au script <strong>firewall.sh</strong>.</p>
|
|||
|
|
|||
|
<p>Si nous souhaitons autoriser notre serveur Web local, il suffit d’ajouter la ligne suivante pour ouvrir le port 80 en TCP.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$IPT -A INPUT -p tcp -i $IFACE_LAN --dport 80 -j ACCEPT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Si la machine fait tourner un serveur NTP qui permet aux machines locales de se synchroniser, il faut ouvrir le port 123 en UDP.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$IPT -A INPUT -p udp -i $IFACE_LAN --dport 123 -j ACCEPT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Le serveur Dnsmasq utilise le port 53 en TCP et en UDP, ainsi que les ports 67 et 68 en UDP. Voici comment nous autorisons l’accès à ce service.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$IPT -A INPUT -p tcp -i $IFACE_LAN --dport 53 -j ACCEPT
|
|||
|
$IPT -A INPUT -p udp -i $IFACE_LAN --dport 53 -j ACCEPT
|
|||
|
$IPT -A INPUT -p udp -i $IFACE_LAN --dport 67:68 -j ACCEPT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="activer-le-relais-des-paquets">Activer le relais des paquets</h3>
|
|||
|
|
|||
|
<p>Dans notre prochain exemple, le serveur est muni de deux cartes réseau et joue un rôle de passerelle.</p>
|
|||
|
|
|||
|
<p>Dans l’exemple, l’interface enp2s0 se situe côté Internet.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /etc/sysconfig/network-scripts/ifcfg-enp2s0
|
|||
|
DEVICE=enp2s0
|
|||
|
TYPE=Ethernet
|
|||
|
ONBOOT=yes
|
|||
|
BOOTPROTO=static
|
|||
|
IPADDR=192.168.2.5
|
|||
|
NETMASK=255.255.255.0
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Côté réseau local, c’est l’interface enp3s1.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /etc/sysconfig/network-scripts/ifcfg-enp3s1
|
|||
|
DEVICE=enp3s1
|
|||
|
TYPE=Ethernet
|
|||
|
ONBOOT=yes
|
|||
|
BOOTPROTO=static
|
|||
|
IPADDR=192.168.3.1
|
|||
|
NETMASK=255.255.255.0
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>L’adresse IP de la passerelle sera notée dans /etc/sysconfig/network.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /etc/sysconfig/network
|
|||
|
GATEWAY=192.168.2.1
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Dans l’état actuel des choses, les machines du sous-réseau <strong>192.168.3.0/255.255.255.0</strong> ne peuvent pas communiquer avec le réseau <strong>192.168.2.0/255.255.255.0</strong>. Pour ce faire, nous devons configurer le relais des paquets.</p>
|
|||
|
|
|||
|
<p>Dans un premier temps, nous devons modifier la politique par défaut de notre pare-feu existant quant aux paquets relayés.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Politique par défaut
|
|||
|
$IPT -P INPUT DROP
|
|||
|
$IPT -P FORWARD ACCEPT
|
|||
|
$IPT -P OUTPUT ACCEPT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ensuite, le relais des paquets doit être activé au niveau du noyau. Concrètement, c’est le paramètre <code class="language-plaintext highlighter-rouge">net.ipv4.ip_forward</code> qui active (1) ou désactive (0) cette fonctionnalité.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sysctl net.ipv4.ip_forward
|
|||
|
net.ipv4.ip_forward = 0
|
|||
|
$ sudo sysctl -w net.ipv4.ip_forward=1
|
|||
|
net.ipv4.ip_forward = 1
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Enfin, c’est <code class="language-plaintext highlighter-rouge">iptables</code> qui va se charger de la traduction d’adresses (IP masquerade).</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -t nat -A POSTROUTING -o enp2s0 -s 192.168.3.0/24 \
|
|||
|
-j MASQUERADE
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Le relais des paquets est défini dans la table <strong>nat</strong> (Network Adress Translation).</li>
|
|||
|
<li>L’interface <strong>enp2s0</strong> est située “à l’extérieur”.</li>
|
|||
|
<li>Le réseau local est <strong>192.168.3.0/24</strong>.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Voici ce que l’on doit obtenir, à peu de choses près.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo iptables -t nat -L POSTROUTING
|
|||
|
Chain POSTROUTING (policy ACCEPT)
|
|||
|
target prot opt source destination
|
|||
|
MASQUERADE all -- 192.168.3.0/24 anywhere
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>À partir de là, toutes les machines du réseau <strong>192.168.3.0/24</strong> peuvent se connecter au serveur <strong>192.168.2.1</strong> en amont et partager la connexion Internet.</p>
|
|||
|
|
|||
|
<p>Au total, le script de pare-feu ressemblera à quelque chose comme ceci sur une passerelle.</p>
|
|||
|
|
|||
|
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
|
|||
|
<span class="c">#</span>
|
|||
|
<span class="c"># firewall.sh</span>
|
|||
|
|
|||
|
<span class="nv">IPT</span><span class="o">=</span>/usr/sbin/iptables
|
|||
|
<span class="nv">SYS</span><span class="o">=</span>/usr/sbin/sysctl
|
|||
|
<span class="nv">SERVICE</span><span class="o">=</span>/sbin/service
|
|||
|
|
|||
|
<span class="c"># Internet</span>
|
|||
|
<span class="nv">IFACE_INET</span><span class="o">=</span>enp2s0
|
|||
|
|
|||
|
<span class="c"># Réseau local</span>
|
|||
|
<span class="nv">IFACE_LAN</span><span class="o">=</span>enp3s1
|
|||
|
<span class="nv">IFACE_LAN_IP</span><span class="o">=</span>192.168.3.0/24
|
|||
|
|
|||
|
<span class="c"># Activer le relais des paquets ? (yes/no)</span>
|
|||
|
<span class="nv">MASQ</span><span class="o">=</span><span class="nb">yes</span>
|
|||
|
|
|||
|
<span class="c"># Tout accepter</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-P</span> INPUT ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-P</span> FORWARD ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-P</span> OUTPUT ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-P</span> PREROUTING ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-P</span> POSTROUTING ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-P</span> OUTPUT ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-P</span> PREROUTING ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-P</span> INPUT ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-P</span> FORWARD ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-P</span> OUTPUT ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-P</span> POSTROUTING ACCEPT
|
|||
|
|
|||
|
<span class="c"># Remettre les compteurs à zéro</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-Z</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-Z</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-Z</span>
|
|||
|
|
|||
|
<span class="c"># Supprimer toutes les règles actives et les chaînes personnalisées</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-F</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> filter <span class="nt">-X</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-F</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-X</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-F</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> mangle <span class="nt">-X</span>
|
|||
|
|
|||
|
<span class="c"># Désactiver le relais des paquets</span>
|
|||
|
<span class="nv">$SYS</span> <span class="nt">-q</span> <span class="nt">-w</span> net.ipv4.ip_forward<span class="o">=</span>0
|
|||
|
|
|||
|
<span class="c"># Politique par défaut</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-P</span> INPUT DROP
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-P</span> FORWARD ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-P</span> OUTPUT ACCEPT
|
|||
|
|
|||
|
<span class="c"># Faire confiance à nous-mêmes ;o)</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-i</span> lo <span class="nt">-j</span> ACCEPT
|
|||
|
|
|||
|
<span class="c"># Ping</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> icmp <span class="nt">--icmp-type</span> echo-request <span class="nt">-j</span> ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> icmp <span class="nt">--icmp-type</span> time-exceeded <span class="nt">-j</span> ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> icmp <span class="nt">--icmp-type</span> destination-unreachable <span class="nt">-j</span> ACCEPT
|
|||
|
|
|||
|
<span class="c"># Connexions établies</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-m</span> state <span class="nt">--state</span> ESTABLISHED <span class="nt">-j</span> ACCEPT
|
|||
|
|
|||
|
<span class="c"># SSH </span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> tcp <span class="nt">-i</span> <span class="nv">$IFACE_INET</span> <span class="nt">--dport</span> 22 <span class="nt">-j</span> ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> tcp <span class="nt">-i</span> <span class="nv">$IFACE_LAN</span> <span class="nt">--dport</span> 22 <span class="nt">-j</span> ACCEPT
|
|||
|
|
|||
|
<span class="c"># Dnsmasq </span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> tcp <span class="nt">-i</span> <span class="nv">$IFACE_LAN</span> <span class="nt">--dport</span> 53 <span class="nt">-j</span> ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> udp <span class="nt">-i</span> <span class="nv">$IFACE_LAN</span> <span class="nt">--dport</span> 53 <span class="nt">-j</span> ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> udp <span class="nt">-i</span> <span class="nv">$IFACE_LAN</span> <span class="nt">--dport</span> 67:68 <span class="nt">-j</span> ACCEPT
|
|||
|
|
|||
|
<span class="c"># Activer le relais des paquets</span>
|
|||
|
<span class="k">if</span> <span class="o">[</span> <span class="nv">$MASQ</span> <span class="o">=</span> <span class="s1">'yes'</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-t</span> nat <span class="nt">-A</span> POSTROUTING <span class="nt">-o</span> <span class="nv">$IFACE_INET</span> <span class="nt">-s</span> <span class="nv">$IFACE_LAN_IP</span> <span class="se">\</span>
|
|||
|
<span class="nt">-j</span> MASQUERADE
|
|||
|
<span class="nv">$SYS</span> <span class="nt">-q</span> <span class="nt">-w</span> net.ipv4.ip_forward<span class="o">=</span>1
|
|||
|
<span class="k">fi</span>
|
|||
|
|
|||
|
<span class="c"># Enregistrer les connexions refusées</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-m</span> limit <span class="nt">--limit</span> 2/min <span class="nt">-j</span> LOG <span class="se">\</span>
|
|||
|
<span class="nt">--log-prefix</span> <span class="s2">"++ IPv4 packet rejected ++ "</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-j</span> DROP
|
|||
|
|
|||
|
<span class="c"># Enregistrer la configuration</span>
|
|||
|
<span class="nv">$SERVICE</span> iptables save
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="limiter-laccès-à-ssh">Limiter l’accès à SSH</h3>
|
|||
|
|
|||
|
<p>Sur une machine comportant une ouverture frontale sur Internet, <code class="language-plaintext highlighter-rouge">iptables</code> peut être utilisé pour limiter les tentatives de connexion à SSH.</p>
|
|||
|
|
|||
|
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># SSH local</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> tcp <span class="nt">-i</span> <span class="nv">$IFACE_LAN</span> <span class="nt">--dport</span> 22 <span class="nt">-j</span> ACCEPT
|
|||
|
|
|||
|
<span class="c"># SSH limité en provenance de l'extérieur</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> tcp <span class="nt">-i</span> <span class="nv">$IFACE_INET</span> <span class="nt">--dport</span> 22 <span class="nt">-m</span> state <span class="se">\</span>
|
|||
|
<span class="nt">--state</span> NEW <span class="nt">-m</span> recent <span class="nt">--set</span> <span class="nt">--name</span> SSH
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> tcp <span class="nt">-i</span> <span class="nv">$IFACE_INET</span> <span class="nt">--dport</span> 22 <span class="nt">-m</span> state <span class="se">\</span>
|
|||
|
<span class="nt">--state</span> NEW <span class="nt">-m</span> recent <span class="nt">--update</span> <span class="nt">--seconds</span> 60 <span class="nt">--hitcount</span> 2 <span class="se">\</span>
|
|||
|
<span class="nt">--rttl</span> <span class="nt">--name</span> SSH <span class="nt">-j</span> DROP
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> tcp <span class="nt">-i</span> <span class="nv">$IFACE_INET</span> <span class="nt">--dport</span> 22 <span class="nt">-j</span> ACCEPT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Concrètement, lorsqu’on essaie d’initier une connexion SSH et que l’on se trompe à trois reprises en saisissant le mot de passe, il faudra attendre au moins une minute (<code class="language-plaintext highlighter-rouge">--seconds 60</code>) avant la prochaine tentative.</p>
|
|||
|
|
|||
|
<p>Sur mes <a href="https://www.microlinux.fr/dedibox-centos-7/">serveurs dédiés publics</a>, j’utilise la configuration suivante.</p>
|
|||
|
|
|||
|
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># SSH illimité </span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> tcp <span class="nt">-s</span> 62.212.100.81 <span class="nt">-i</span> <span class="nv">$IFACE_INET</span> <span class="nt">--dport</span> 22 <span class="se">\</span>
|
|||
|
<span class="nt">-j</span> ACCEPT
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> tcp <span class="nt">-s</span> 165.173.80.8 <span class="nt">-i</span> <span class="nv">$IFACE_INET</span> <span class="nt">--dport</span> 22 <span class="se">\</span>
|
|||
|
<span class="nt">-j</span> ACCEPT
|
|||
|
|
|||
|
<span class="c"># SSH limité</span>
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> tcp <span class="nt">-i</span> <span class="nv">$IFACE_INET</span> <span class="nt">--dport</span> 22 <span class="nt">-m</span> state <span class="se">\</span>
|
|||
|
<span class="nt">--state</span> NEW <span class="nt">-m</span> recent <span class="nt">--set</span> <span class="nt">--name</span> SSH
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> tcp <span class="nt">-i</span> <span class="nv">$IFACE_INET</span> <span class="nt">--dport</span> 22 <span class="nt">-m</span> state <span class="se">\</span>
|
|||
|
<span class="nt">--state</span> NEW <span class="nt">-m</span> recent <span class="nt">--update</span> <span class="nt">--seconds</span> 300 <span class="nt">--hitcount</span> 2 <span class="se">\</span>
|
|||
|
<span class="nt">--rttl</span> <span class="nt">--name</span> SSH <span class="nt">-j</span> DROP
|
|||
|
<span class="nv">$IPT</span> <span class="nt">-A</span> INPUT <span class="nt">-p</span> tcp <span class="nt">-i</span> <span class="nv">$IFACE_INET</span> <span class="nt">--dport</span> 22 <span class="nt">-j</span> ACCEPT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Le délai d’attente avant la prochaine tentative de connexion est augmenté de 1 à 5 minutes (<code class="language-plaintext highlighter-rouge">--seconds 300</code>).</li>
|
|||
|
<li>Les deux premières lignes définissent une exception pour les connexions en provenance de mon bureau et du serveur de sauvegardes (qui utilise <code class="language-plaintext highlighter-rouge">rsync</code> en combinaison avec <code class="language-plaintext highlighter-rouge">ssh</code> pour se connecter).</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h3 id="documentation">Documentation</h3>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Antoine Le Morvan – Iptables, <a href="https://formatux.fr/fr">Formatux</a>
|
|||
|
</li>
|
|||
|
<li>Carla Schroder – Building a Linux Firewall, <a href="https://www.amazon.fr/Linux-Networking-Cookbook-Carla-Schroder/dp/0596102488">Linux Networking Cookbook</a>
|
|||
|
</li>
|
|||
|
<li>Michael Kofler, Firewalls mit Iptables selbst gebaut, <a href="https://kofler.info/buecher/linux/">Linux 2017</a>
|
|||
|
</li>
|
|||
|
<li>Oskar Andreasson – <a href="https://www.frozentux.net/iptables-tutorial/iptables-tutorial.html">Iptables Tutorial</a>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h3 id="téléchargement">Téléchargement</h3>
|
|||
|
|
|||
|
<p>Mon dépôt Github fournit quelques modèles de scripts <code class="language-plaintext highlighter-rouge">firewall-*.sh</code> pour différents contextes, dans le répertoire <strong>centos/el7/config/firewall</strong>.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># git clone https://github.com/kikinovak/centos
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>
|
|||
|
<code class="language-plaintext highlighter-rouge">firewall-gateway.sh</code> pour un serveur LAN faisant office de passerelle</li>
|
|||
|
<li>
|
|||
|
<code class="language-plaintext highlighter-rouge">firewall-standalone.sh</code> pour un serveur standalone dans un LAN</li>
|
|||
|
<li>
|
|||
|
<code class="language-plaintext highlighter-rouge">firewall-dedibox.sh</code> pour une machine publique</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Le sous-répertoire <strong>services</strong> fournit quelques exemples de règles de filtrage pour les services les plus répandus.</p>
|
|||
|
|
|||
|
<h2 id="supprimer-une-règle-précise-dans-iptables">Supprimer une règle précise dans IPtables</h2>
|
|||
|
|
|||
|
<h3 id="présentation">Présentation</h3>
|
|||
|
|
|||
|
<p>Dans ce court tutoriel, nous allons apprendre à supprimer une règle spécifique dans IPtables. Dans IPtables, le pare-feu présent par défaut sur la plupart des distributions Linux, les règles sont traitées et gérées par ligne à la suite, autrement dit quand on ajoute une règle, elle se met à la suite des autres et ne les remplace pas.</p>
|
|||
|
|
|||
|
<h3 id="procédure">Procédure</h3>
|
|||
|
|
|||
|
<p>Heureusement, il est possible de gérer les règles indépendantes lorsque l’on souhaite en supprimer une, nous ne sommes pas obligé de tout supprimer puis de réécrire toutes nos règles mise à part celle(s) dont nous ne voulons plus. Il faut commencer par lister nos règles avec une option supplémentaire qui nous permettra d’afficher des numéros de lignes :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -L --line-number
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>On aura alors l’affichage des règles pour les tables INPUT, OUTPUT et FORWARD :</p>
|
|||
|
|
|||
|
<p><img src="/images/iptables3.png" alt="" width="600"></p>
|
|||
|
|
|||
|
<p>On voit ici les numéros de lignes qui sont présents devant chaque règles iptables</p>
|
|||
|
|
|||
|
<p>Pour avoir les tables PRE et POSTROUTING, il faut utiliser la commande suivante :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -L -t nat --line-number
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Suite à cela, on va pouvoir identifier précisément une règle ou une autre et on va pouvoir en supprimer une précisément avec la commande suivante :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -D [TABLE] X
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>ou “TABLE” est la table visée et “X” le numéro de ligne.
|
|||
|
Par exemple si nous voulons supprimer la ligne 9 de la table INPUT :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -D INPUT 9
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Si on liste à nouveau nos règles, on verra que seule la règle anciennement numéro 4 a disparu :</p>
|
|||
|
|
|||
|
<p><img src="/images/iptables4.png" alt="" width="600"></p>
|
|||
|
|
|||
|
<h2 id="liens">Liens</h2>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><a href="https://www.thegeekstuff.com/2011/01/iptables-fundamentals/">Linux Firewall Tutorial: IPTables Tables, Chains, Rules Fundamentals</a></li>
|
|||
|
<li><a href="https://www.thegeekstuff.com/2011/02/iptables-add-rule/">Linux IPTables: How to Add Firewall Rules (With Allow SSH Example)</a></li>
|
|||
|
<li><a href="https://www.thegeekstuff.com/2011/03/iptables-inbound-and-outbound-rules/">Linux IPTables: Incoming and Outgoing Rule Examples (SSH and HTTP)</a></li>
|
|||
|
<li><a href="https://www.thegeekstuff.com/2012/08/iptables-log-packets/">How to Log Linux IPTables Firewall Dropped Packets to a Log File</a></li>
|
|||
|
<li><a href="https://www.thegeekstuff.com/2011/06/iptables-rules-examples/">25 Most Frequently Used Linux IPTables Rules Examples</a></li>
|
|||
|
<li><a href="https://wiki.archlinux.fr/Iptables">Iptables (archlinux)</a></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<div class="d-print-none">
|
|||
|
<footer class="article__footer"><meta itemprop="dateModified" content="2018-10-08T00:00:00+02:00">
|
|||
|
<!-- start custom article footer snippet -->
|
|||
|
|
|||
|
<!-- end custom article footer snippet -->
|
|||
|
<!--
|
|||
|
<div align="right"><a type="application/rss+xml" href="/feed.xml" title="S'abonner"><i class="fa fa-rss fa-2x"></i></a>
|
|||
|
|
|||
|
 </div>
|
|||
|
-->
|
|||
|
</footer>
|
|||
|
<div class="article__section-navigator clearfix">
|
|||
|
<div class="previous">
|
|||
|
<span>PRÉCÉDENT</span><a href="/2018/10/02/DocFetcher-recherche-de-contenu.html">DocFetcher, application Open Source pour la recherche de contenu</a>
|
|||
|
</div>
|
|||
|
<div class="next">
|
|||
|
<span>SUIVANT</span><a href="/2018/10/21/Comment-compiler-des-modules-NGINX-dynamiques.html">Comment compiler des modules NGINX dynamiques</a>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
</div>
|
|||
|
|
|||
|
<script>(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
$(function() {
|
|||
|
var $this ,$scroll;
|
|||
|
var $articleContent = $('.js-article-content');
|
|||
|
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
|
|||
|
var scroll = hasSidebar ? '.js-page-main' : 'html, body';
|
|||
|
$scroll = $(scroll);
|
|||
|
|
|||
|
$articleContent.find('.highlight').each(function() {
|
|||
|
$this = $(this);
|
|||
|
$this.attr('data-lang', $this.find('code').attr('data-lang'));
|
|||
|
});
|
|||
|
$articleContent.find('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]').each(function() {
|
|||
|
$this = $(this);
|
|||
|
$this.append($('<a class="anchor d-print-none" aria-hidden="true"></a>').html('<i class="fas fa-anchor"></i>'));
|
|||
|
});
|
|||
|
$articleContent.on('click', '.anchor', function() {
|
|||
|
$scroll.scrollToAnchor('#' + $(this).parent().attr('id'), 400);
|
|||
|
});
|
|||
|
});
|
|||
|
});
|
|||
|
})();
|
|||
|
</script>
|
|||
|
|
|||
|
</div>
|
|||
|
<section class="page__comments d-print-none"></section></article><!-- start custom main bottom snippet -->
|
|||
|
<!-- end custom main bottom snippet -->
|
|||
|
</div>
|
|||
|
</div></div></div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<script>(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
var $body = $('body'), $window = $(window);
|
|||
|
var $pageRoot = $('.js-page-root'), $pageMain = $('.js-page-main');
|
|||
|
var activeCount = 0;
|
|||
|
function modal(options) {
|
|||
|
var $root = this, visible, onChange, hideWhenWindowScroll = false;
|
|||
|
var scrollTop;
|
|||
|
function setOptions(options) {
|
|||
|
var _options = options || {};
|
|||
|
visible = _options.initialVisible === undefined ? false : show;
|
|||
|
onChange = _options.onChange;
|
|||
|
hideWhenWindowScroll = _options.hideWhenWindowScroll;
|
|||
|
}
|
|||
|
function init() {
|
|||
|
setState(visible);
|
|||
|
}
|
|||
|
function setState(isShow) {
|
|||
|
if (isShow === visible) {
|
|||
|
return;
|
|||
|
}
|
|||
|
visible = isShow;
|
|||
|
if (visible) {
|
|||
|
activeCount++;
|
|||
|
scrollTop = $(window).scrollTop() || $pageMain.scrollTop();
|
|||
|
$root.addClass('modal--show');
|
|||
|
$pageMain.scrollTop(scrollTop);
|
|||
|
activeCount === 1 && ($pageRoot.addClass('show-modal'), $body.addClass('of-hidden'));
|
|||
|
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.on('scroll', hide);
|
|||
|
$window.on('keyup', handleKeyup);
|
|||
|
} else {
|
|||
|
activeCount > 0 && activeCount--;
|
|||
|
$root.removeClass('modal--show');
|
|||
|
$window.scrollTop(scrollTop);
|
|||
|
activeCount === 0 && ($pageRoot.removeClass('show-modal'), $body.removeClass('of-hidden'));
|
|||
|
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.off('scroll', hide);
|
|||
|
$window.off('keyup', handleKeyup);
|
|||
|
}
|
|||
|
onChange && onChange(visible);
|
|||
|
}
|
|||
|
function show() {
|
|||
|
setState(true);
|
|||
|
}
|
|||
|
function hide() {
|
|||
|
setState(false);
|
|||
|
}
|
|||
|
function handleKeyup(e) {
|
|||
|
// Char Code: 27 ESC
|
|||
|
if (e.which === 27) {
|
|||
|
hide();
|
|||
|
}
|
|||
|
}
|
|||
|
setOptions(options);
|
|||
|
init();
|
|||
|
return {
|
|||
|
show: show,
|
|||
|
hide: hide,
|
|||
|
$el: $root
|
|||
|
};
|
|||
|
}
|
|||
|
$.fn.modal = modal;
|
|||
|
});
|
|||
|
})();
|
|||
|
</script><div class="modal modal--overflow page__search-modal d-print-none js-page-search-modal">
|
|||
|
<script>
|
|||
|
(function () {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
// search panel
|
|||
|
var search = (window.search || (window.search = {}));
|
|||
|
var useDefaultSearchBox = window.useDefaultSearchBox === undefined ?
|
|||
|
true : window.useDefaultSearchBox ;
|
|||
|
|
|||
|
var $searchModal = $('.js-page-search-modal');
|
|||
|
var $searchToggle = $('.js-search-toggle');
|
|||
|
var searchModal = $searchModal.modal({ onChange: handleModalChange, hideWhenWindowScroll: true });
|
|||
|
var modalVisible = false;
|
|||
|
search.searchModal = searchModal;
|
|||
|
|
|||
|
var $searchBox = null;
|
|||
|
var $searchInput = null;
|
|||
|
var $searchClear = null;
|
|||
|
|
|||
|
function getModalVisible() {
|
|||
|
return modalVisible;
|
|||
|
}
|
|||
|
search.getModalVisible = getModalVisible;
|
|||
|
|
|||
|
function handleModalChange(visible) {
|
|||
|
modalVisible = visible;
|
|||
|
if (visible) {
|
|||
|
search.onShow && search.onShow();
|
|||
|
useDefaultSearchBox && $searchInput[0] && $searchInput[0].focus();
|
|||
|
} else {
|
|||
|
search.onShow && search.onHide();
|
|||
|
useDefaultSearchBox && $searchInput[0] && $searchInput[0].blur();
|
|||
|
setTimeout(function() {
|
|||
|
useDefaultSearchBox && ($searchInput.val(''), $searchBox.removeClass('not-empty'));
|
|||
|
search.clear && search.clear();
|
|||
|
window.pageAsideAffix && window.pageAsideAffix.refresh();
|
|||
|
}, 400);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
$searchToggle.on('click', function() {
|
|||
|
modalVisible ? searchModal.hide() : searchModal.show();
|
|||
|
});
|
|||
|
// Char Code: 83 S, 191 /
|
|||
|
$(window).on('keyup', function(e) {
|
|||
|
if (!modalVisible && !window.isFormElement(e.target || e.srcElement) && (e.which === 83 || e.which === 191)) {
|
|||
|
modalVisible || searchModal.show();
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
if (useDefaultSearchBox) {
|
|||
|
$searchBox = $('.js-search-box');
|
|||
|
$searchInput = $searchBox.children('input');
|
|||
|
$searchClear = $searchBox.children('.js-icon-clear');
|
|||
|
search.getSearchInput = function() {
|
|||
|
return $searchInput.get(0);
|
|||
|
};
|
|||
|
search.getVal = function() {
|
|||
|
return $searchInput.val();
|
|||
|
};
|
|||
|
search.setVal = function(val) {
|
|||
|
$searchInput.val(val);
|
|||
|
};
|
|||
|
|
|||
|
$searchInput.on('focus', function() {
|
|||
|
$(this).addClass('focus');
|
|||
|
});
|
|||
|
$searchInput.on('blur', function() {
|
|||
|
$(this).removeClass('focus');
|
|||
|
});
|
|||
|
$searchInput.on('input', window.throttle(function() {
|
|||
|
var val = $(this).val();
|
|||
|
if (val === '' || typeof val !== 'string') {
|
|||
|
search.clear && search.clear();
|
|||
|
} else {
|
|||
|
$searchBox.addClass('not-empty');
|
|||
|
search.onInputNotEmpty && search.onInputNotEmpty(val);
|
|||
|
}
|
|||
|
}, 400));
|
|||
|
$searchClear.on('click', function() {
|
|||
|
$searchInput.val(''); $searchBox.removeClass('not-empty');
|
|||
|
search.clear && search.clear();
|
|||
|
});
|
|||
|
}
|
|||
|
});
|
|||
|
})();
|
|||
|
</script><div class="search search--dark">
|
|||
|
<div class="main">
|
|||
|
<div class="search__header">Recherche</div>
|
|||
|
<div class="search-bar">
|
|||
|
<div class="search-box js-search-box">
|
|||
|
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
|
|||
|
<input id="search-input" type="text">
|
|||
|
<div class="search-box__icon-clear js-icon-clear">
|
|||
|
<a><i class="fas fa-times"></i></a>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<button class="button button--theme-dark button--pill search__cancel js-search-toggle">
|
|||
|
Annuler</button>
|
|||
|
</div>
|
|||
|
<div id="results-container" class="search-result js-search-result"></div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<!-- Script pointing to search-script.js -->
|
|||
|
<script>/*!
|
|||
|
* Simple-Jekyll-Search
|
|||
|
* Copyright 2015-2020, Christian Fei
|
|||
|
* Licensed under the MIT License.
|
|||
|
*/
|
|||
|
|
|||
|
(function(){
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$Templater_7 = {
|
|||
|
compile: compile,
|
|||
|
setOptions: setOptions
|
|||
|
}
|
|||
|
|
|||
|
const options = {}
|
|||
|
options.pattern = /\{(.*?)\}/g
|
|||
|
options.template = ''
|
|||
|
options.middleware = function () {}
|
|||
|
|
|||
|
function setOptions (_options) {
|
|||
|
options.pattern = _options.pattern || options.pattern
|
|||
|
options.template = _options.template || options.template
|
|||
|
if (typeof _options.middleware === 'function') {
|
|||
|
options.middleware = _options.middleware
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function compile (data) {
|
|||
|
return options.template.replace(options.pattern, function (match, prop) {
|
|||
|
const value = options.middleware(prop, data[prop], options.template)
|
|||
|
if (typeof value !== 'undefined') {
|
|||
|
return value
|
|||
|
}
|
|||
|
return data[prop] || match
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
'use strict';
|
|||
|
|
|||
|
function fuzzysearch (needle, haystack) {
|
|||
|
var tlen = haystack.length;
|
|||
|
var qlen = needle.length;
|
|||
|
if (qlen > tlen) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
if (qlen === tlen) {
|
|||
|
return needle === haystack;
|
|||
|
}
|
|||
|
outer: for (var i = 0, j = 0; i < qlen; i++) {
|
|||
|
var nch = needle.charCodeAt(i);
|
|||
|
while (j < tlen) {
|
|||
|
if (haystack.charCodeAt(j++) === nch) {
|
|||
|
continue outer;
|
|||
|
}
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
var _$fuzzysearch_1 = fuzzysearch;
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
|
|||
|
|
|||
|
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
|
|||
|
|
|||
|
function FuzzySearchStrategy () {
|
|||
|
this.matches = function (string, crit) {
|
|||
|
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
|
|||
|
|
|||
|
function LiteralSearchStrategy () {
|
|||
|
this.matches = function (str, crit) {
|
|||
|
if (!str) return false
|
|||
|
|
|||
|
str = str.trim().toLowerCase()
|
|||
|
crit = crit.trim().toLowerCase()
|
|||
|
|
|||
|
return crit.split(' ').filter(function (word) {
|
|||
|
return str.indexOf(word) >= 0
|
|||
|
}).length === crit.split(' ').length
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$Repository_4 = {
|
|||
|
put: put,
|
|||
|
clear: clear,
|
|||
|
search: search,
|
|||
|
setOptions: __setOptions_4
|
|||
|
}
|
|||
|
|
|||
|
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
|
|||
|
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
|
|||
|
|
|||
|
function NoSort () {
|
|||
|
return 0
|
|||
|
}
|
|||
|
|
|||
|
const data = []
|
|||
|
let opt = {}
|
|||
|
|
|||
|
opt.fuzzy = false
|
|||
|
opt.limit = 10
|
|||
|
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
|
|||
|
opt.sort = NoSort
|
|||
|
opt.exclude = []
|
|||
|
|
|||
|
function put (data) {
|
|||
|
if (isObject(data)) {
|
|||
|
return addObject(data)
|
|||
|
}
|
|||
|
if (isArray(data)) {
|
|||
|
return addArray(data)
|
|||
|
}
|
|||
|
return undefined
|
|||
|
}
|
|||
|
function clear () {
|
|||
|
data.length = 0
|
|||
|
return data
|
|||
|
}
|
|||
|
|
|||
|
function isObject (obj) {
|
|||
|
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
|
|||
|
}
|
|||
|
|
|||
|
function isArray (obj) {
|
|||
|
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
|
|||
|
}
|
|||
|
|
|||
|
function addObject (_data) {
|
|||
|
data.push(_data)
|
|||
|
return data
|
|||
|
}
|
|||
|
|
|||
|
function addArray (_data) {
|
|||
|
const added = []
|
|||
|
clear()
|
|||
|
for (let i = 0, len = _data.length; i < len; i++) {
|
|||
|
if (isObject(_data[i])) {
|
|||
|
added.push(addObject(_data[i]))
|
|||
|
}
|
|||
|
}
|
|||
|
return added
|
|||
|
}
|
|||
|
|
|||
|
function search (crit) {
|
|||
|
if (!crit) {
|
|||
|
return []
|
|||
|
}
|
|||
|
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
|
|||
|
}
|
|||
|
|
|||
|
function __setOptions_4 (_opt) {
|
|||
|
opt = _opt || {}
|
|||
|
|
|||
|
opt.fuzzy = _opt.fuzzy || false
|
|||
|
opt.limit = _opt.limit || 10
|
|||
|
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
|
|||
|
opt.sort = _opt.sort || NoSort
|
|||
|
opt.exclude = _opt.exclude || []
|
|||
|
}
|
|||
|
|
|||
|
function findMatches (data, crit, strategy, opt) {
|
|||
|
const matches = []
|
|||
|
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
|
|||
|
const match = findMatchesInObject(data[i], crit, strategy, opt)
|
|||
|
if (match) {
|
|||
|
matches.push(match)
|
|||
|
}
|
|||
|
}
|
|||
|
return matches
|
|||
|
}
|
|||
|
|
|||
|
function findMatchesInObject (obj, crit, strategy, opt) {
|
|||
|
for (const key in obj) {
|
|||
|
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
|
|||
|
return obj
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function isExcluded (term, excludedTerms) {
|
|||
|
for (let i = 0, len = excludedTerms.length; i < len; i++) {
|
|||
|
const excludedTerm = excludedTerms[i]
|
|||
|
if (new RegExp(excludedTerm).test(term)) {
|
|||
|
return true
|
|||
|
}
|
|||
|
}
|
|||
|
return false
|
|||
|
}
|
|||
|
|
|||
|
/* globals ActiveXObject:false */
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$JSONLoader_2 = {
|
|||
|
load: load
|
|||
|
}
|
|||
|
|
|||
|
function load (location, callback) {
|
|||
|
const xhr = getXHR()
|
|||
|
xhr.open('GET', location, true)
|
|||
|
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
|
|||
|
xhr.send()
|
|||
|
}
|
|||
|
|
|||
|
function createStateChangeListener (xhr, callback) {
|
|||
|
return function () {
|
|||
|
if (xhr.readyState === 4 && xhr.status === 200) {
|
|||
|
try {
|
|||
|
callback(null, JSON.parse(xhr.responseText))
|
|||
|
} catch (err) {
|
|||
|
callback(err, null)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function getXHR () {
|
|||
|
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$OptionsValidator_3 = function OptionsValidator (params) {
|
|||
|
if (!validateParams(params)) {
|
|||
|
throw new Error('-- OptionsValidator: required options missing')
|
|||
|
}
|
|||
|
|
|||
|
if (!(this instanceof OptionsValidator)) {
|
|||
|
return new OptionsValidator(params)
|
|||
|
}
|
|||
|
|
|||
|
const requiredOptions = params.required
|
|||
|
|
|||
|
this.getRequiredOptions = function () {
|
|||
|
return requiredOptions
|
|||
|
}
|
|||
|
|
|||
|
this.validate = function (parameters) {
|
|||
|
const errors = []
|
|||
|
requiredOptions.forEach(function (requiredOptionName) {
|
|||
|
if (typeof parameters[requiredOptionName] === 'undefined') {
|
|||
|
errors.push(requiredOptionName)
|
|||
|
}
|
|||
|
})
|
|||
|
return errors
|
|||
|
}
|
|||
|
|
|||
|
function validateParams (params) {
|
|||
|
if (!params) {
|
|||
|
return false
|
|||
|
}
|
|||
|
return typeof params.required !== 'undefined' && params.required instanceof Array
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$utils_9 = {
|
|||
|
merge: merge,
|
|||
|
isJSON: isJSON
|
|||
|
}
|
|||
|
|
|||
|
function merge (defaultParams, mergeParams) {
|
|||
|
const mergedOptions = {}
|
|||
|
for (const option in defaultParams) {
|
|||
|
mergedOptions[option] = defaultParams[option]
|
|||
|
if (typeof mergeParams[option] !== 'undefined') {
|
|||
|
mergedOptions[option] = mergeParams[option]
|
|||
|
}
|
|||
|
}
|
|||
|
return mergedOptions
|
|||
|
}
|
|||
|
|
|||
|
function isJSON (json) {
|
|||
|
try {
|
|||
|
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
|
|||
|
return true
|
|||
|
}
|
|||
|
return false
|
|||
|
} catch (err) {
|
|||
|
return false
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var _$src_8 = {};
|
|||
|
(function (window) {
|
|||
|
'use strict'
|
|||
|
|
|||
|
let options = {
|
|||
|
searchInput: null,
|
|||
|
resultsContainer: null,
|
|||
|
json: [],
|
|||
|
success: Function.prototype,
|
|||
|
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
|
|||
|
templateMiddleware: Function.prototype,
|
|||
|
sortMiddleware: function () {
|
|||
|
return 0
|
|||
|
},
|
|||
|
noResultsText: 'No results found',
|
|||
|
limit: 10,
|
|||
|
fuzzy: false,
|
|||
|
debounceTime: null,
|
|||
|
exclude: []
|
|||
|
}
|
|||
|
|
|||
|
let debounceTimerHandle
|
|||
|
const debounce = function (func, delayMillis) {
|
|||
|
if (delayMillis) {
|
|||
|
clearTimeout(debounceTimerHandle)
|
|||
|
debounceTimerHandle = setTimeout(func, delayMillis)
|
|||
|
} else {
|
|||
|
func.call()
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
|
|||
|
|
|||
|
/* removed: const _$Templater_7 = require('./Templater') */;
|
|||
|
/* removed: const _$Repository_4 = require('./Repository') */;
|
|||
|
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
|
|||
|
const optionsValidator = _$OptionsValidator_3({
|
|||
|
required: requiredOptions
|
|||
|
})
|
|||
|
/* removed: const _$utils_9 = require('./utils') */;
|
|||
|
|
|||
|
window.SimpleJekyllSearch = function (_options) {
|
|||
|
const errors = optionsValidator.validate(_options)
|
|||
|
if (errors.length > 0) {
|
|||
|
throwError('You must specify the following required options: ' + requiredOptions)
|
|||
|
}
|
|||
|
|
|||
|
options = _$utils_9.merge(options, _options)
|
|||
|
|
|||
|
_$Templater_7.setOptions({
|
|||
|
template: options.searchResultTemplate,
|
|||
|
middleware: options.templateMiddleware
|
|||
|
})
|
|||
|
|
|||
|
_$Repository_4.setOptions({
|
|||
|
fuzzy: options.fuzzy,
|
|||
|
limit: options.limit,
|
|||
|
sort: options.sortMiddleware,
|
|||
|
exclude: options.exclude
|
|||
|
})
|
|||
|
|
|||
|
if (_$utils_9.isJSON(options.json)) {
|
|||
|
initWithJSON(options.json)
|
|||
|
} else {
|
|||
|
initWithURL(options.json)
|
|||
|
}
|
|||
|
|
|||
|
const rv = {
|
|||
|
search: search
|
|||
|
}
|
|||
|
|
|||
|
typeof options.success === 'function' && options.success.call(rv)
|
|||
|
return rv
|
|||
|
}
|
|||
|
|
|||
|
function initWithJSON (json) {
|
|||
|
_$Repository_4.put(json)
|
|||
|
registerInput()
|
|||
|
}
|
|||
|
|
|||
|
function initWithURL (url) {
|
|||
|
_$JSONLoader_2.load(url, function (err, json) {
|
|||
|
if (err) {
|
|||
|
throwError('failed to get JSON (' + url + ')')
|
|||
|
}
|
|||
|
initWithJSON(json)
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
function emptyResultsContainer () {
|
|||
|
options.resultsContainer.innerHTML = ''
|
|||
|
}
|
|||
|
|
|||
|
function appendToResultsContainer (text) {
|
|||
|
options.resultsContainer.innerHTML += text
|
|||
|
}
|
|||
|
|
|||
|
function registerInput () {
|
|||
|
options.searchInput.addEventListener('input', function (e) {
|
|||
|
if (isWhitelistedKey(e.which)) {
|
|||
|
emptyResultsContainer()
|
|||
|
debounce(function () { search(e.target.value) }, options.debounceTime)
|
|||
|
}
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
function search (query) {
|
|||
|
if (isValidQuery(query)) {
|
|||
|
emptyResultsContainer()
|
|||
|
render(_$Repository_4.search(query), query)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function render (results, query) {
|
|||
|
const len = results.length
|
|||
|
if (len === 0) {
|
|||
|
return appendToResultsContainer(options.noResultsText)
|
|||
|
}
|
|||
|
for (let i = 0; i < len; i++) {
|
|||
|
results[i].query = query
|
|||
|
appendToResultsContainer(_$Templater_7.compile(results[i]))
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function isValidQuery (query) {
|
|||
|
return query && query.length > 0
|
|||
|
}
|
|||
|
|
|||
|
function isWhitelistedKey (key) {
|
|||
|
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
|
|||
|
}
|
|||
|
|
|||
|
function throwError (message) {
|
|||
|
throw new Error('SimpleJekyllSearch --- ' + message)
|
|||
|
}
|
|||
|
})(window)
|
|||
|
|
|||
|
}());
|
|||
|
</script>
|
|||
|
|
|||
|
<!-- Configuration -->
|
|||
|
<script>
|
|||
|
SimpleJekyllSearch({
|
|||
|
searchInput: document.getElementById('search-input'),
|
|||
|
resultsContainer: document.getElementById('results-container'),
|
|||
|
noResultsText: '<p>Aucun résultat!</p>',
|
|||
|
json: '/search.json',
|
|||
|
searchResultTemplate: '<li><a href="{url}">{date} {title}</a> (Création {create})</li>'
|
|||
|
})
|
|||
|
</script>
|
|||
|
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
<script>(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
function scrollToAnchor(anchor, duration, callback) {
|
|||
|
var $root = this;
|
|||
|
$root.animate({ scrollTop: $(anchor).position().top }, duration, function() {
|
|||
|
window.history.replaceState(null, '', window.location.href.split('#')[0] + anchor);
|
|||
|
callback && callback();
|
|||
|
});
|
|||
|
}
|
|||
|
$.fn.scrollToAnchor = scrollToAnchor;
|
|||
|
});
|
|||
|
})();
|
|||
|
(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
function affix(options) {
|
|||
|
var $root = this, $window = $(window), $scrollTarget, $scroll,
|
|||
|
offsetBottom = 0, scrollTarget = window, scroll = window.document, disabled = false, isOverallScroller = true,
|
|||
|
rootTop, rootLeft, rootHeight, scrollBottom, rootBottomTop,
|
|||
|
hasInit = false, curState;
|
|||
|
|
|||
|
function setOptions(options) {
|
|||
|
var _options = options || {};
|
|||
|
_options.offsetBottom && (offsetBottom = _options.offsetBottom);
|
|||
|
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
|
|||
|
_options.scroll && (scroll = _options.scroll);
|
|||
|
_options.disabled !== undefined && (disabled = _options.disabled);
|
|||
|
$scrollTarget = $(scrollTarget);
|
|||
|
isOverallScroller = window.isOverallScroller($scrollTarget[0]);
|
|||
|
$scroll = $(scroll);
|
|||
|
}
|
|||
|
function preCalc() {
|
|||
|
top();
|
|||
|
rootHeight = $root.outerHeight();
|
|||
|
rootTop = $root.offset().top + (isOverallScroller ? 0 : $scrollTarget.scrollTop());
|
|||
|
rootLeft = $root.offset().left;
|
|||
|
}
|
|||
|
function calc(needPreCalc) {
|
|||
|
needPreCalc && preCalc();
|
|||
|
scrollBottom = $scroll.outerHeight() - offsetBottom - rootHeight;
|
|||
|
rootBottomTop = scrollBottom - rootTop;
|
|||
|
}
|
|||
|
function top() {
|
|||
|
if (curState !== 'top') {
|
|||
|
$root.removeClass('fixed').css({
|
|||
|
left: 0,
|
|||
|
top: 0
|
|||
|
});
|
|||
|
curState = 'top';
|
|||
|
}
|
|||
|
}
|
|||
|
function fixed() {
|
|||
|
if (curState !== 'fixed') {
|
|||
|
$root.addClass('fixed').css({
|
|||
|
left: rootLeft + 'px',
|
|||
|
top: 0
|
|||
|
});
|
|||
|
curState = 'fixed';
|
|||
|
}
|
|||
|
}
|
|||
|
function bottom() {
|
|||
|
if (curState !== 'bottom') {
|
|||
|
$root.removeClass('fixed').css({
|
|||
|
left: 0,
|
|||
|
top: rootBottomTop + 'px'
|
|||
|
});
|
|||
|
curState = 'bottom';
|
|||
|
}
|
|||
|
}
|
|||
|
function setState() {
|
|||
|
var scrollTop = $scrollTarget.scrollTop();
|
|||
|
if (scrollTop >= rootTop && scrollTop <= scrollBottom) {
|
|||
|
fixed();
|
|||
|
} else if (scrollTop < rootTop) {
|
|||
|
top();
|
|||
|
} else {
|
|||
|
bottom();
|
|||
|
}
|
|||
|
}
|
|||
|
function init() {
|
|||
|
if(!hasInit) {
|
|||
|
var interval, timeout;
|
|||
|
calc(true); setState();
|
|||
|
// run calc every 100 millisecond
|
|||
|
interval = setInterval(function() {
|
|||
|
calc();
|
|||
|
}, 100);
|
|||
|
timeout = setTimeout(function() {
|
|||
|
clearInterval(interval);
|
|||
|
}, 45000);
|
|||
|
window.pageLoad.then(function() {
|
|||
|
setTimeout(function() {
|
|||
|
clearInterval(interval);
|
|||
|
clearTimeout(timeout);
|
|||
|
}, 3000);
|
|||
|
});
|
|||
|
$scrollTarget.on('scroll', function() {
|
|||
|
disabled || setState();
|
|||
|
});
|
|||
|
$window.on('resize', function() {
|
|||
|
disabled || (calc(true), setState());
|
|||
|
});
|
|||
|
hasInit = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
setOptions(options);
|
|||
|
if (!disabled) {
|
|||
|
init();
|
|||
|
}
|
|||
|
$window.on('resize', window.throttle(function() {
|
|||
|
init();
|
|||
|
}, 200));
|
|||
|
return {
|
|||
|
setOptions: setOptions,
|
|||
|
refresh: function() {
|
|||
|
calc(true, { animation: false }); setState();
|
|||
|
}
|
|||
|
};
|
|||
|
}
|
|||
|
$.fn.affix = affix;
|
|||
|
});
|
|||
|
})();
|
|||
|
(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
function toc(options) {
|
|||
|
var $root = this, $window = $(window), $scrollTarget, $scroller, $tocUl = $('<ul class="toc toc--ellipsis"></ul>'), $tocLi, $headings, $activeLast, $activeCur,
|
|||
|
selectors = 'h1,h2,h3', container = 'body', scrollTarget = window, scroller = 'html, body', disabled = false,
|
|||
|
headingsPos, scrolling = false, hasRendered = false, hasInit = false;
|
|||
|
|
|||
|
function setOptions(options) {
|
|||
|
var _options = options || {};
|
|||
|
_options.selectors && (selectors = _options.selectors);
|
|||
|
_options.container && (container = _options.container);
|
|||
|
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
|
|||
|
_options.scroller && (scroller = _options.scroller);
|
|||
|
_options.disabled !== undefined && (disabled = _options.disabled);
|
|||
|
$headings = $(container).find(selectors).filter('[id]');
|
|||
|
$scrollTarget = $(scrollTarget);
|
|||
|
$scroller = $(scroller);
|
|||
|
}
|
|||
|
function calc() {
|
|||
|
headingsPos = [];
|
|||
|
$headings.each(function() {
|
|||
|
headingsPos.push(Math.floor($(this).position().top));
|
|||
|
});
|
|||
|
}
|
|||
|
function setState(element, disabled) {
|
|||
|
var scrollTop = $scrollTarget.scrollTop(), i;
|
|||
|
if (disabled || !headingsPos || headingsPos.length < 1) { return; }
|
|||
|
if (element) {
|
|||
|
$activeCur = element;
|
|||
|
} else {
|
|||
|
for (i = 0; i < headingsPos.length; i++) {
|
|||
|
if (scrollTop >= headingsPos[i]) {
|
|||
|
$activeCur = $tocLi.eq(i);
|
|||
|
} else {
|
|||
|
$activeCur || ($activeCur = $tocLi.eq(i));
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
$activeLast && $activeLast.removeClass('active');
|
|||
|
($activeLast = $activeCur).addClass('active');
|
|||
|
}
|
|||
|
function render() {
|
|||
|
if(!hasRendered) {
|
|||
|
$root.append($tocUl);
|
|||
|
$headings.each(function() {
|
|||
|
var $this = $(this);
|
|||
|
$tocUl.append($('<li></li>').addClass('toc-' + $this.prop('tagName').toLowerCase())
|
|||
|
.append($('<a></a>').text($this.text()).attr('href', '#' + $this.prop('id'))));
|
|||
|
});
|
|||
|
$tocLi = $tocUl.children('li');
|
|||
|
$tocUl.on('click', 'a', function(e) {
|
|||
|
e.preventDefault();
|
|||
|
var $this = $(this);
|
|||
|
scrolling = true;
|
|||
|
setState($this.parent());
|
|||
|
$scroller.scrollToAnchor($this.attr('href'), 400, function() {
|
|||
|
scrolling = false;
|
|||
|
});
|
|||
|
});
|
|||
|
}
|
|||
|
hasRendered = true;
|
|||
|
}
|
|||
|
function init() {
|
|||
|
var interval, timeout;
|
|||
|
if(!hasInit) {
|
|||
|
render(); calc(); setState(null, scrolling);
|
|||
|
// run calc every 100 millisecond
|
|||
|
interval = setInterval(function() {
|
|||
|
calc();
|
|||
|
}, 100);
|
|||
|
timeout = setTimeout(function() {
|
|||
|
clearInterval(interval);
|
|||
|
}, 45000);
|
|||
|
window.pageLoad.then(function() {
|
|||
|
setTimeout(function() {
|
|||
|
clearInterval(interval);
|
|||
|
clearTimeout(timeout);
|
|||
|
}, 3000);
|
|||
|
});
|
|||
|
$scrollTarget.on('scroll', function() {
|
|||
|
disabled || setState(null, scrolling);
|
|||
|
});
|
|||
|
$window.on('resize', window.throttle(function() {
|
|||
|
if (!disabled) {
|
|||
|
render(); calc(); setState(null, scrolling);
|
|||
|
}
|
|||
|
}, 100));
|
|||
|
}
|
|||
|
hasInit = true;
|
|||
|
}
|
|||
|
|
|||
|
setOptions(options);
|
|||
|
if (!disabled) {
|
|||
|
init();
|
|||
|
}
|
|||
|
$window.on('resize', window.throttle(function() {
|
|||
|
init();
|
|||
|
}, 200));
|
|||
|
return {
|
|||
|
setOptions: setOptions
|
|||
|
};
|
|||
|
}
|
|||
|
$.fn.toc = toc;
|
|||
|
});
|
|||
|
})();
|
|||
|
/*(function () {
|
|||
|
|
|||
|
})();*/
|
|||
|
</script><script>
|
|||
|
/* toc must before affix, since affix need to konw toc' height. */(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
var TOC_SELECTOR = window.TEXT_VARIABLES.site.toc.selectors;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
var $window = $(window);
|
|||
|
var $articleContent = $('.js-article-content');
|
|||
|
var $tocRoot = $('.js-toc-root'), $col2 = $('.js-col-aside');
|
|||
|
var toc;
|
|||
|
var tocDisabled = false;
|
|||
|
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
|
|||
|
var hasToc = $articleContent.find(TOC_SELECTOR).length > 0;
|
|||
|
|
|||
|
function disabled() {
|
|||
|
return $col2.css('display') === 'none' || !hasToc;
|
|||
|
}
|
|||
|
|
|||
|
tocDisabled = disabled();
|
|||
|
|
|||
|
toc = $tocRoot.toc({
|
|||
|
selectors: TOC_SELECTOR,
|
|||
|
container: $articleContent,
|
|||
|
scrollTarget: hasSidebar ? '.js-page-main' : null,
|
|||
|
scroller: hasSidebar ? '.js-page-main' : null,
|
|||
|
disabled: tocDisabled
|
|||
|
});
|
|||
|
|
|||
|
$window.on('resize', window.throttle(function() {
|
|||
|
tocDisabled = disabled();
|
|||
|
toc && toc.setOptions({
|
|||
|
disabled: tocDisabled
|
|||
|
});
|
|||
|
}, 100));
|
|||
|
|
|||
|
});
|
|||
|
})();
|
|||
|
(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
var $window = $(window), $pageFooter = $('.js-page-footer');
|
|||
|
var $pageAside = $('.js-page-aside');
|
|||
|
var affix;
|
|||
|
var tocDisabled = false;
|
|||
|
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
|
|||
|
|
|||
|
affix = $pageAside.affix({
|
|||
|
offsetBottom: $pageFooter.outerHeight(),
|
|||
|
scrollTarget: hasSidebar ? '.js-page-main' : null,
|
|||
|
scroller: hasSidebar ? '.js-page-main' : null,
|
|||
|
scroll: hasSidebar ? $('.js-page-main').children() : null,
|
|||
|
disabled: tocDisabled
|
|||
|
});
|
|||
|
|
|||
|
$window.on('resize', window.throttle(function() {
|
|||
|
affix && affix.setOptions({
|
|||
|
disabled: tocDisabled
|
|||
|
});
|
|||
|
}, 100));
|
|||
|
|
|||
|
window.pageAsideAffix = affix;
|
|||
|
});
|
|||
|
})();
|
|||
|
</script><!---->
|
|||
|
|
|||
|
</div>
|
|||
|
<script>(function () {
|
|||
|
var $root = document.getElementsByClassName('root')[0];
|
|||
|
if (window.hasEvent('touchstart')) {
|
|||
|
$root.dataset.isTouch = true;
|
|||
|
document.addEventListener('touchstart', function(){}, false);
|
|||
|
}
|
|||
|
})();
|
|||
|
</script>
|
|||
|
</body>
|
|||
|
</html>
|
|||
|
|