4712 lines
316 KiB
HTML
4712 lines
316 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>FirstHeberg KVM4 Debian 9 serveur web + messagerie domaine xoyize.xyz (février 2018) - YannStatic</title>
|
|||
|
|
|||
|
<meta name="description" content="Debian 9 serveur web (nginx+php7+mariadb) + serveur de messagerie (postfix dovecot dkim spf dmarc rainloop spamassassin clamav fail2ban)">
|
|||
|
<link rel="canonical" href="https://static.rnmkcy.eu/2018/11/23/FirstHeberg-KVM4-Debian9-serveur-web-messagerie-xoyize.xyz.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.5
|
|||
|
" 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;">FirstHeberg KVM4 Debian 9 serveur web + messagerie domaine xoyize.xyz (février 2018)</h1></header></div><meta itemprop="headline" content="FirstHeberg KVM4 Debian 9 serveur web + messagerie domaine xoyize.xyz (février 2018)"><div class="article__info clearfix"><ul class="left-col menu"><li>
|
|||
|
<a class="button button--secondary button--pill button--sm"
|
|||
|
href="/archive.html?tag=serveur">serveur</a>
|
|||
|
</li></ul><ul class="right-col menu"><li>
|
|||
|
<i class="far fa-calendar-alt"></i> <span title="Création" style="color:#FF00FF">23 nov. 2018</span>
|
|||
|
|
|||
|
<span title="Modification" style="color:#00FF7F">28 févr. 2018</span></li></ul></div><meta itemprop="datePublished" content="2018-02-28T00:00:00+01:00">
|
|||
|
<meta itemprop="keywords" content="serveur"><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="debian-9-serveur-web-nginxphp7mariadb--serveur-de-messagerie-postfix-dovecot-dkim-spf-dmarc-rainloop-spamassassin-clamav-fail2ban">Debian 9 serveur web (nginx+php7+mariadb) + serveur de messagerie (postfix dovecot dkim spf dmarc rainloop spamassassin clamav fail2ban)</h1>
|
|||
|
|
|||
|
<h2 id="kvm-debian-9--serveur-nginxphp7mariadb">KVM Debian 9 serveur (nginx+php7+mariadb)</h2>
|
|||
|
|
|||
|
<p><img src="/images/debian9a.png" alt="Debian 9" /></p>
|
|||
|
|
|||
|
<p>Package: 4 GB Mémoire, 2 CPU, 30 GB SSD, 100 Mbps<br />
|
|||
|
Selected Location: Paris<br />
|
|||
|
Debian 9 64<br />
|
|||
|
Livraison : vps-26381 93.115.96.97</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Domaine : xoyize.xyz</li>
|
|||
|
<li>IPv4 du serveur : 93.115.96.97</li>
|
|||
|
<li>IPv6 du serveur : 2a03:75c0:35:670d::1</li>
|
|||
|
<li>Certificats : Let’s Encrypt</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h3 id="première-connexion-ssh">Première connexion SSH</h3>
|
|||
|
|
|||
|
<p>Via SSH</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh root@93.115.96.97 Màj
|
|||
|
|
|||
|
apt update && apt upgrade
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Modifier <strong>hostname</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hostnamectl set-hostname --static vps26381
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Modifier <strong>/etc/hosts</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>127.0.0.1 localhost
|
|||
|
93.115.96.97 xoyize.xyz vps26381
|
|||
|
|
|||
|
|
|||
|
# The following lines are desirable for IPv6 capable hosts
|
|||
|
::1 localhost ip6-localhost ip6-loopback
|
|||
|
ff02::1 ip6-allnodes
|
|||
|
ff02::2 ip6-allrouters
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vérifications</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hostname
|
|||
|
vps26381
|
|||
|
hostname --fqdn
|
|||
|
xoyize.xyz
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Installer les divers outils</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install rsync curl tmux jq figlet git dnsutils -y
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="locales">Locales</h3>
|
|||
|
|
|||
|
<p>Locales : <strong>fr_FR.UTF-8</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dpkg-reconfigure locales
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Generating locales (this might take a while)...
|
|||
|
fr_FR.UTF-8... done
|
|||
|
Generation complete.
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="timezone">TimeZone</h3>
|
|||
|
|
|||
|
<p>Europe/Paris</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dpkg-reconfigure tzdata
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Current default time zone: 'Europe/Paris'
|
|||
|
Local time is now: Wed Feb 14 06:41:55 CET 2018.
|
|||
|
Universal Time is now: Wed Feb 14 05:41:55 UTC 2018.
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="root">root</h3>
|
|||
|
|
|||
|
<p>Modifier le mot de passe <strong>root</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>passwd root
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="création-utilisateur">Création utilisateur</h3>
|
|||
|
|
|||
|
<p>Utilisateur <strong>xoyan</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>useradd -m -d /home/xoyan/ -s /bin/bash xoyan
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Mot de passe <strong>xoyan</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>passwd xoyan
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Visudo pour les accès root via utilisateur <strong>xoyan</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install sudo -y #installé par défaut sur certaines distributions
|
|||
|
echo "xoyan ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Déconnexion puis connexion ssh en mode utilisateur</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh xoyan@93.115.96.97
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="dns-ovh">DNS OVH</h3>
|
|||
|
|
|||
|
<p>Configuration des champs DNS domaine <strong>xoyize.xyz</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$TTL 3600
|
|||
|
@ IN SOA dns106.ovh.net. tech.ovh.net. (2018021102 86400 3600 3600000 300)
|
|||
|
IN NS ns106.ovh.net.
|
|||
|
IN NS dns106.ovh.net.
|
|||
|
IN MX 10 xoyize.xyz.
|
|||
|
IN A 93.115.96.97
|
|||
|
IN AAAA 2a03:75c0:35:670d::1
|
|||
|
* IN CNAME xoyize.xyz.
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vérifier que les enregistrements MX de la configuration DNS pointent vers le serveur.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dig +short MX xoyize.xyz
|
|||
|
10 xoyize.xyz.
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="openssh-avec-clé">OpenSSH avec clé</h3>
|
|||
|
|
|||
|
<p><img src="/images/ssh_logo1.png" alt="OpenSSH" /></p>
|
|||
|
|
|||
|
<p><strong>connexion avec clé</strong><br />
|
|||
|
<u>sur l'ordinateur de bureau</u>
|
|||
|
Générer une paire de clé curve25519-sha256 (ECDH avec Curve25519 et SHA2) nommé <strong>kvm-vps-26381</strong> pour une liaison SSH avec le serveur KVM.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh-keygen -t ed25519 -o -a 100 -f ~/.ssh/kvm-vps-26381
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Envoyer la clé publique sur le serveur KVM</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>scp ~/.ssh/kvm-vps-26381.pub xoyan@93.115.96.97:/home/xoyan/
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><u>sur le serveur KVM</u>
|
|||
|
On se connecte</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh xoyan@93.115.96.97
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Copier le contenu de la clé publique dans /home/$USER/.ssh/authorized_keys</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cd ~
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Sur le KVM ,créer un dossier .ssh</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pwd #pour vérifier que l'on est sous /home/$USER
|
|||
|
mkdir .ssh
|
|||
|
cat /home/$USER/kvm-vps-26381.pub >> /home/$USER/.ssh/authorized_keys
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>et donner les droits</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chmod 600 /home/$USER/.ssh/authorized_keys
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>effacer le fichier de la clé</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rm /home/$USER/kvm-vps-26381.pub
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Modifier la configuration serveur SSH</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/ssh/sshd_config
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Modifier</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Port = 55026
|
|||
|
PermitRootLogin no
|
|||
|
PasswordAuthentication no
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><u>session SSH ne se termine pas correctement lors d'un "reboot" à distance</u><br />
|
|||
|
Si vous tentez de <strong>redémarrer/éteindre</strong> une machine distance par <strong>ssh</strong>, vous pourriez constater que votre session ne se termine pas correctement, vous laissant avec un terminal inactif jusqu’à l’expiration d’un long délai d’inactivité. Il existe un bogue 751636 à ce sujet. Pour l’instant, la solution de contournement à ce problème est d’installer :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt install libpam-systemd #Installer par défaut sur debian 9
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>cela terminera la session ssh avant que le réseau ne tombe.<br />
|
|||
|
Veuillez noter qu’il est nécessaire que PAM soit activé dans sshd.</p>
|
|||
|
|
|||
|
<p>Relancer openSSH</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl restart sshd
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Accès depuis le poste distant avec la clé privée</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh -p 55026 -i ~/.ssh/kvm-vps-26381 xoyan@93.115.96.97
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><strong>Exécution script sur connexion</strong><br />
|
|||
|
Exécuter un fichier <em>utilisateur</em> nommé <strong>$HOME/.ssh/rc</strong> si <em>présent</em><br />
|
|||
|
Pour <em>tous les utilisateurs</em> exécuter un fichier nommé <strong>/etc/ssh/sshrc</strong> si <em>présent</em><br />
|
|||
|
Installer les utilitaires <em>curl jq figlet</em></p>
|
|||
|
|
|||
|
<p>Le batch</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano ~/.ssh/rc
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/bin/bash
|
|||
|
|
|||
|
#clear
|
|||
|
PROCCOUNT=`ps -Afl | wc -l` # nombre de lignes
|
|||
|
PROCCOUNT=`expr $PROCCOUNT - 5` # on ote les non concernées
|
|||
|
GROUPZ=`users`
|
|||
|
ipinfo=$(curl -s ipinfo.io) # info localisation format json
|
|||
|
publicip=$(echo $ipinfo | jq -r '.ip') # extraction des données , installer préalablement "jq"
|
|||
|
ville=$(echo $ipinfo | jq -r '.city')
|
|||
|
pays=$(echo $ipinfo | jq -r '.country')
|
|||
|
cpuname=`cat /proc/cpuinfo |grep 'model name' | cut -d: -f2 | sed -n 1p`
|
|||
|
|
|||
|
echo "\033[0m\033[1;31m"
|
|||
|
figlet "xoyize.xyz"
|
|||
|
echo "\033[0m"
|
|||
|
echo "\033[1;35m \033[1;37mHostname \033[1;35m= \033[1;32m`hostname`
|
|||
|
\033[1;35m \033[1;37mWired Ip \033[1;35m= \033[1;32m`ip addr show eth0 | grep 'inet\b' | awk '{print $2}' | cut -d/ -f1`
|
|||
|
\033[1;35m \033[1;37mKernel \033[1;35m= \033[1;32m`uname -r`
|
|||
|
\033[1;35m \033[1;37mDebian \033[1;35m= \033[1;32m`cat /etc/debian_version`
|
|||
|
\033[1;35m \033[1;37mUptime \033[1;35m= \033[1;32m`uptime | sed 's/.*up ([^,]*), .*/1/' | sed -e 's/^[ \t]*//'`
|
|||
|
\033[1;35m \033[1;37mCPU \033[1;35m= \033[1;32m`echo $cpuname`
|
|||
|
\033[1;35m\033[1;37mMemory Use \033[1;35m= \033[1;32m`free -m | awk 'NR==2{printf "%s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 }'`
|
|||
|
\033[1;35m \033[1;37mUsername \033[1;35m= \033[1;32m`whoami`
|
|||
|
\033[1;35m \033[1;37mSessions \033[1;35m= \033[1;32m`who | grep $USER | wc -l`
|
|||
|
\033[1;35m\033[1;37mPublic Ip \033[1;35m= \033[1;32m`echo $publicip $pays`
|
|||
|
\033[0m"
|
|||
|
#curl fr.wttr.in/Paris?0
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Effacer motd</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo rm /etc/motd
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Déconnexion puis connexion</p>
|
|||
|
|
|||
|
<h3 id="nginx-php7-mariadb">Nginx PHP7 MariaDb</h3>
|
|||
|
|
|||
|
<p><img src="/images/nginx-php7-mariadb1.png" alt="lemp" /></p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><a href="https://static.ouestline.net/linux/2017/10/03/Serveur-web-nginx-PHP7.html">Serveur web nginx PHP7</a></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Script <strong>debian9-compil-nginx-php7.sh</strong> pour compiler et installer nginx + php7 sur debian stretch</p>
|
|||
|
|
|||
|
<p>Définition des chemins et fichiers de configuration nginx<br />
|
|||
|
<strong>/etc/nginx/conf.d/xoyize.xyz.conf</strong> configuration de base du domaine<br />
|
|||
|
Création dossier <strong>/etc/nginx/conf.d/xoyize.xyz.d/</strong> pour les fichiers de configuration supplémentaires</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo mkdir -p /etc/nginx/conf.d/xoyize.xyz.d
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Déplacer et renommer le fichier de configuration par défaut</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/xoyize.xyz.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Modifier le fichier</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/nginx/conf.d/xoyize.xyz.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server {
|
|||
|
listen 80;
|
|||
|
listen [::]:80;
|
|||
|
root /var/www/ ;
|
|||
|
location ~ \.php$ {
|
|||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
|||
|
fastcgi_pass unix:/run/php/php7.0-fpm.sock; # PHP7.0
|
|||
|
fastcgi_index index.php;
|
|||
|
include fastcgi_params;
|
|||
|
fastcgi_param SCRIPT_FILENAME $request_filename;
|
|||
|
}
|
|||
|
|
|||
|
include conf.d/xoyize.xyz.d/*.conf;
|
|||
|
|
|||
|
access_log /var/log/nginx/xoyize.xyz.log;
|
|||
|
error_log /var/log/nginx/xoyize.xyz.log;
|
|||
|
}
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Recharger</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl reload nginx
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Activer le service</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl enable nginx
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="installer-le-résolveur-dns-unbound">Installer le résolveur DNS Unbound</h3>
|
|||
|
|
|||
|
<p><img src="/images/unbound-250.png" alt="DNS Unbound" /></p>
|
|||
|
|
|||
|
<p><em>Les serveurs DNS sont des machines discutant entre elles afin de se communiquer les correspondances entre nom de domaine et adresses IP.</em></p>
|
|||
|
|
|||
|
<p>Passage en mode super utilisateur</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -s # ou su
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Désinstaller bind</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt remove --purge bind* -y
|
|||
|
rm -r /var/cache/bind/
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Installation des outils dns et du paquet Unbound :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install dnsutils unbound -y
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Téléchargement de la liste des serveurs DNS racines :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd /var/lib/unbound/ && wget ftp://ftp.internic.net/domain/named.cache
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Mise en place de cette liste pour le serveur Unbound :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mv named.cache root.hints && chown unbound:unbound root.hints
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Les fichiers de configuration sont situés sous <strong>/etc/unbound/unbound.conf.d/</strong><br />
|
|||
|
<strong>root-auto-trust-anchor-file.conf</strong> qui est créé par défaut à l’installation</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server:
|
|||
|
# The following line will configure unbound to perform cryptographic
|
|||
|
# DNSSEC validation using the root trust anchor.
|
|||
|
auto-trust-anchor-file: "/var/lib/unbound/root.key"
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><strong>qname-minimisation.conf</strong> qui est créé par défaut à l’installation</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server:
|
|||
|
# Send minimum amount of information to upstream servers to enhance
|
|||
|
# privacy. Only sends minimum required labels of the QNAME and sets
|
|||
|
# QTYPE to NS when possible.
|
|||
|
|
|||
|
# See RFC 7816 "DNS Query Name Minimisation to Improve Privacy" for
|
|||
|
# details.
|
|||
|
|
|||
|
qname-minimisation: yes
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajout d’un fichier de configuration<br />
|
|||
|
<strong>dns-yan.conf</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server:
|
|||
|
interface: 0.0.0.0
|
|||
|
interface: ::0
|
|||
|
access-control: 0.0.0.0/0 allow
|
|||
|
access-control: ::/0 allow
|
|||
|
root-hints: "/var/lib/unbound/root.hints"
|
|||
|
verbosity: 0
|
|||
|
#qname-minimisation: yes
|
|||
|
|
|||
|
num-threads: 2
|
|||
|
msg-cache-slabs: 4
|
|||
|
rrset-cache-slabs: 4
|
|||
|
infra-cache-slabs: 4
|
|||
|
key-cache-slabs: 4
|
|||
|
rrset-cache-size: 100m
|
|||
|
msg-cache-size: 50m
|
|||
|
outgoing-range: 465
|
|||
|
so-rcvbuf: 4m
|
|||
|
so-sndbuf: 4m
|
|||
|
port: 53
|
|||
|
do-ip4: yes
|
|||
|
do-ip6: yes
|
|||
|
do-udp: yes
|
|||
|
do-tcp: yes
|
|||
|
do-daemonize: yes
|
|||
|
hide-identity: yes
|
|||
|
hide-version: yes
|
|||
|
harden-glue: yes
|
|||
|
harden-dnssec-stripped: yes
|
|||
|
harden-referral-path: yes
|
|||
|
use-caps-for-id: yes
|
|||
|
prefetch: yes
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Redémarrer le service <strong>dnsunbound</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl restart unbound
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Les commandes suivantes ne fonctionneront que si le paquet “dnsutils” est installé sur votre système Debian!</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dig @127.0.0.1 xoyize.xyz +short +dnssec
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>93.115.96.97
|
|||
|
A 7 2 3600 20180313112728 20180211112728 28565 xoyize.xyz. c6J6Shp8HFXdzAon8u2q7Am2TPm8ufjslmwyITy803jXxO8sqzF6tkVB SiIiK6q+g0trEPA7iYTUfRTfg5yhhlRikkvZaPjavgpwxA4DBYzPR+HO LQlXDo+N9mh7+pKrWoO/tfSlji3Di08r6C1tMl5LBkLM5DXBPWuD75ka LpI=
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Si la commande dig-command a fonctionné, vous pouvez maintenant définir Unbound comme premier résolveur DNS pour votre système de messagerie:</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install resolvconf -y
|
|||
|
echo "nameserver 127.0.0.1" >> /etc/resolvconf/resolv.conf.d/head
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Le résultat de la commande</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nslookup xoyize.xyz | grep Server
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>devrait ressembler à ceci:</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Server: 127.0.0.1
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vérifier la résolution de nom à partir du serveur :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dig @127.0.0.1 xoyize.xyz
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>; <<>> DiG 9.10.3-P4-Debian <<>> @127.0.0.1 xoyize.xyz
|
|||
|
; (1 server found)
|
|||
|
...
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>La résolution fonctionne</p>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>Maintenant, vous disposez de votre propre résolveur DNS.</p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p>Mise à jour automatique des serveurs DNS de la racine ,créer un bash</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/unbound/dnsunbound-update-root-dns.sh
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
|
|||
|
|
|||
|
<span class="nv">TmpName</span><span class="o">=</span><span class="si">$(</span><span class="nb">mktemp</span><span class="si">)</span>
|
|||
|
<span class="nv">TmpDiff</span><span class="o">=</span><span class="si">$(</span><span class="nb">mktemp</span><span class="si">)</span>
|
|||
|
<span class="nv">TmpErr</span><span class="o">=</span><span class="si">$(</span><span class="nb">mktemp</span><span class="si">)</span>
|
|||
|
<span class="nv">REPORT_EMAIL</span><span class="o">=</span><span class="s2">"admin"</span>
|
|||
|
<span class="nv">URL</span><span class="o">=</span><span class="s2">"https://www.internic.net/domain/named.cache"</span>
|
|||
|
|
|||
|
wget <span class="nt">-nv</span> <span class="nv">$URL</span> <span class="nt">-O</span> <span class="nv">$TmpName</span> 2> <span class="nv">$TmpErr</span>
|
|||
|
|
|||
|
<span class="c"># On intercepte toute erreur</span>
|
|||
|
<span class="c"># et on stoppe le script dans ce cas</span>
|
|||
|
<span class="c"># On continue sinon</span>
|
|||
|
|
|||
|
<span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$?</span><span class="s2">"</span> <span class="nt">-ne</span> 0 <span class="o">]</span><span class="p">;</span><span class="k">then
|
|||
|
</span><span class="nb">printf</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">Script was stopped at this point. A manual action may be required.</span><span class="se">\n</span><span class="s2">"</span> <span class="o">>></span> <span class="nv">$TmpErr</span>
|
|||
|
mail <span class="nt">-s</span> <span class="s2">"[DNS - </span><span class="si">$(</span><span class="nb">uname</span> <span class="nt">-n</span><span class="si">)</span><span class="s2">] Root hints file download failed"</span> <span class="nv">$REPORT_EMAIL</span> < <span class="nv">$TmpErr</span>
|
|||
|
<span class="nb">rm</span> <span class="nv">$TmpErr</span>
|
|||
|
<span class="nb">rm</span> <span class="nv">$TmpDiff</span>
|
|||
|
<span class="nb">rm</span> <span class="nv">$TmpName</span>
|
|||
|
<span class="nb">exit </span>0
|
|||
|
<span class="k">else
|
|||
|
</span><span class="nb">rm</span> <span class="nv">$TmpErr</span>
|
|||
|
<span class="nv">shaTMP</span><span class="o">=</span><span class="si">$(</span><span class="nb">sha512sum</span> <span class="nv">$TmpName</span> | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>
|
|||
|
<span class="nv">shaHINTS</span><span class="o">=</span><span class="si">$(</span><span class="nb">sha512sum</span> /var/lib/unbound/root.hints | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>
|
|||
|
|
|||
|
<span class="k">if</span> <span class="o">[</span> <span class="nv">$shaTMP</span> <span class="o">=</span> <span class="nv">$shaHINTS</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
|
|||
|
<span class="c"># Si le fichier récupéré est identique à celui</span>
|
|||
|
<span class="c"># utilisé par Unbound, on fait... rien</span>
|
|||
|
<span class="nb">rm</span> <span class="nv">$TmpName</span>
|
|||
|
<span class="nb">exit </span>0
|
|||
|
<span class="k">else
|
|||
|
</span><span class="nb">printf</span> <span class="s2">"A new root hints file was spotted on InterNIC server.</span><span class="se">\n</span><span class="s2">File downloaded and old root.hints file replaced.</span><span class="se">\n</span><span class="s2">Here is the diff:</span><span class="se">\n\n</span><span class="s2">"</span> <span class="o">></span> <span class="nv">$Tmp</span><span class="err">$</span>
|
|||
|
diff <span class="nv">$TmpName</span> /var/lib/unbound/root.hints <span class="o">>></span> <span class="nv">$TmpDiff</span>
|
|||
|
<span class="nb">printf</span> <span class="s2">"</span><span class="se">\n\n</span><span class="s2">"</span> <span class="o">>></span> <span class="nv">$TmpDiff</span>
|
|||
|
<span class="nb">mv</span> <span class="nt">-f</span> <span class="nv">$TmpName</span> /var/lib/unbound/root.hints
|
|||
|
<span class="nb">chown </span>unbound: /var/lib/unbound/root.hints
|
|||
|
<span class="nb">chmod </span>644 /var/lib/unbound/root.hints
|
|||
|
<span class="nb">sleep </span>5
|
|||
|
service unbound restart
|
|||
|
<span class="nb">printf</span> <span class="s2">"Unbound status is </span><span class="si">$(</span>service unbound status | <span class="nb">grep </span>Active | <span class="nb">awk</span> <span class="s1">'{print $2 " " $3}'</span><span class="si">)</span><span class="se">\n</span><span class="s2">"</span> <span class="o">>></span> <span class="nv">$TmpDiff</span>
|
|||
|
mail <span class="nt">-s</span> <span class="s2">"[DNS - </span><span class="si">$(</span><span class="nb">uname</span> <span class="nt">-n</span><span class="si">)</span><span class="s2">] Update in Root Hints"</span> <span class="nv">$REPORT_EMAIL</span> < <span class="nv">$TmpDiff</span>
|
|||
|
<span class="nb">rm</span> <span class="nv">$TmpDiff</span>
|
|||
|
<span class="k">fi
|
|||
|
fi
|
|||
|
</span><span class="nb">exit </span>0
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Droits en exécution</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chmod +x /etc/unbound/dnsunbound-update-root-dns.sh
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Planification journalière</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>crontab -e
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajouter en fin de fichier</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Mise à jour automatique des serveurs DNS de la racine
|
|||
|
50 02 * * * /etc/unbound/dnsunbound-update-root-dns.sh
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="certificats-ssl-letsencrypt-acme">Certificats SSL letsencrypt (acme)</h3>
|
|||
|
|
|||
|
<p><img src="/images/letsencrypt-logo1.png" alt="SSL Letsencrypt" /></p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><a href="https://static.ouestline.net/linux/2017/08/31/Acme-Certficats-Serveurs.html">Acme et Let’s Encrypt</a></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Génération des certificats</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/root/.acme.sh/acme.sh --dns dns_ovh --issue --keylength 4096 -d xoyize.xyz -d smtp.xoyize.xyz -d imap.xoyize.xyz -d webmail.xoyize.xyz -d mail.xoyize.xyz -d nc.xoyize.xyz
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Certificats</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[mercredi 14 février 2018, 08:03:43 (UTC+0100)] Your cert is in /root/.acme.sh/xoyize.xyz/xoyize.xyz.cer
|
|||
|
[mercredi 14 février 2018, 08:03:43 (UTC+0100)] Your cert key is in /root/.acme.sh/xoyize.xyz/xoyize.xyz.key
|
|||
|
[mercredi 14 février 2018, 08:03:43 (UTC+0100)] The intermediate CA cert is in /root/.acme.sh/xoyize.xyz/ca.cer
|
|||
|
[mercredi 14 février 2018, 08:03:43 (UTC+0100)] And the full chain certs is there: /root/.acme.sh/xoyize.xyz/fullchain.cer
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Création des liens sur <strong>/etc/ssl/private</strong> pour nginx</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ln -s /root/.acme.sh/xoyize.xyz/fullchain.cer /etc/ssl/private/xoyize.xyz-fullchain.pem
|
|||
|
ln -s /root/.acme.sh/xoyize.xyz/xoyize.xyz.key /etc/ssl/private/xoyize.xyz-key.pem
|
|||
|
ln -s /root/.acme.sh/xoyize.xyz/xoyize.xyz.cer /etc/ssl/private/xoyize.xyz-chain.pem
|
|||
|
ln -s /root/.acme.sh/xoyize.xyz/ca.cer /etc/ssl/private/xoyize.xyz-ca.pem
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vérification de la mise à jour automatique</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>crontab -e
|
|||
|
|
|||
|
11 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>NOTE: Sous-domaines <strong>webmail.xoyize.xyz -> Rainloop</strong> et <strong>nc.xoyize.xyz -> Nextcloud</strong></p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<h3 id="nginx--ssl--header--diffie-hellmann">Nginx + SSL + header + diffie-hellmann</h3>
|
|||
|
|
|||
|
<p><strong>ssl</strong><br />
|
|||
|
Il faut préalablement demander des certificats (ca+key) SSL pour le domaine auprès d’une autorité de certification (let’s encrypt ou autre)<br />
|
|||
|
Le fichier de configuration</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/nginx/ssl_params
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ssl_certificate /etc/ssl/private/xoyize.xyz-fullchain.pem;
|
|||
|
ssl_certificate_key /etc/ssl/private/xoyize.xyz-key.pem;
|
|||
|
ssl_session_timeout 5m;
|
|||
|
ssl_session_cache shared:SSL:50m;
|
|||
|
ssl_prefer_server_ciphers on;
|
|||
|
|
|||
|
# Ciphers with intermediate compatibility
|
|||
|
# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=intermediate
|
|||
|
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
|||
|
# ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
|
|||
|
|
|||
|
# Ciphers with modern compatibility
|
|||
|
# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=modern
|
|||
|
# Uncomment the following to use modern ciphers, but remove compatibility with some old clients (android before 5.0, Internet Explorer before 10, ...)
|
|||
|
ssl_protocols TLSv1.2;
|
|||
|
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
|
|||
|
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><strong>Entêtes</strong><br />
|
|||
|
Le fichier de configuration</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/nginx/header_params
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> # Quelques explications https://www.alsacreations.com/article/lire/1723-tour-horizon-https-et-en-tetes-de-securite.html
|
|||
|
#HSTS est un dispositif de sécurité par lequel un site web peut déclarer aux navigateurs qu’ils doivent communiquer avec lui en utilisant exclusivement le protocole HTTPS, au lieu du HTTP
|
|||
|
add_header Strict-Transport-Security "max-age=31536000;";
|
|||
|
# Seul le site utilise peut utiliser iFrame
|
|||
|
add_header Content-Security-Policy "frame-ancestors 'self'";
|
|||
|
#se protéger contre le détournement de clic (clickjacking)
|
|||
|
add_header X-Frame-Options "SAMEORIGIN";
|
|||
|
# Empêche les navigateurs de détecter incorrectement les scripts qui ne sont pas des scripts
|
|||
|
add_header X-Content-Type-Options "nosniff";
|
|||
|
# Bloquer les pages à charger lorsqu'elles détectent des attaques XSS
|
|||
|
add_header X-XSS-Protection "1; mode=block";
|
|||
|
|
|||
|
#Supprimer les ## des lignes pour activer la fonction correspondante
|
|||
|
|
|||
|
#CSP permet d’autoriser seulement les domaines déclarés à exécuter du script JavaScript, une feuille de style css, etc.
|
|||
|
# Content-Security-Policy : https://openweb.eu.org/articles/content-security-policy
|
|||
|
## add_header Content-Security-Policy "default-src 'self'";
|
|||
|
|
|||
|
# Désactiver les références pour les navigateurs qui ne prennent pas en charge strict-origin-when-cross-origin
|
|||
|
# Referrer-Policy : https://scotthelme.co.uk/a-new-security-header-referrer-policy/
|
|||
|
# Utilise strict-origin-when-cross-origin pour les navigateurs qui le font
|
|||
|
## add_header Referrer-Policy "no-referrer, strict-origin-when-cross-origin";
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><strong>Diffie-Hellmann</strong><br />
|
|||
|
Générer une clé Diffie-Hellmann<br />
|
|||
|
<em>En cryptographie, l’échange de clés Diffie-Hellman, du nom de ses auteurs Whitfield Diffie et Martin Hellman, est une méthode par laquelle deux agents nommés conventionnellement <strong>A</strong> et<strong>B</strong> peuvent se mettre d’accord sur un nombre (qu’ils peuvent utiliser comme clé pour chiffrer la conversation suivante) sans qu’un troisième agent appelé <strong>D</strong> puisse découvrir le nombre, même en ayant écouté tous leurs échanges.</em></p>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>ATTENTION : Génération clé Diffie-Hellmann très très longue en temps , 30 minutes minimum…</p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p>Générer une clé</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>openssl dhparam -out /etc/ssl/private/dh4096.pem -outform PEM -2 4096
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Déplacer la clé dans le répertoire</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo mv dh4096.pem /etc/ssl/private/
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Droits pour root</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo chmod 600 /etc/ssl/private/dh4096.pem
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Le fichier de configuration</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/nginx/dh_param
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> # Uncomment the following directive after DH generation
|
|||
|
# > openssl dhparam -out /etc/ssl/private/dh4096.pem -outform PEM -2 4096
|
|||
|
ssl_dhparam /etc/ssl/private/dh4096.pem;
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Configuration de base avec SSL et sécurité + letsencrypt (renouvellement)</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/nginx/conf.d/xoyize.xyz.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server {
|
|||
|
listen 80;
|
|||
|
listen [::]:80;
|
|||
|
|
|||
|
## redirect http to https ##
|
|||
|
return 301 https://$server_name$request_uri;
|
|||
|
}
|
|||
|
|
|||
|
server {
|
|||
|
listen 443 ssl http2;
|
|||
|
listen [::]:443 ssl http2;
|
|||
|
server_name xoyize.xyz;
|
|||
|
|
|||
|
#### Locations
|
|||
|
# On cache les fichiers statiques
|
|||
|
location ~* \.(html|css|js|png|jpg|jpeg|gif|ico|svg|eot|woff|ttf)$ { expires max; }
|
|||
|
# On interdit les dotfiles
|
|||
|
location ~ /\. { deny all; }
|
|||
|
|
|||
|
include ssl_params;
|
|||
|
include header_params;
|
|||
|
include dh_param;
|
|||
|
|
|||
|
root /var/www/ ;
|
|||
|
index index.php index.html index.htm;
|
|||
|
location ~ \.php$ {
|
|||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
|||
|
fastcgi_pass unix:/run/php/php7.0-fpm.sock; # PHP7.0
|
|||
|
fastcgi_index index.php;
|
|||
|
include fastcgi_params;
|
|||
|
fastcgi_param SCRIPT_FILENAME $request_filename;
|
|||
|
}
|
|||
|
|
|||
|
include conf.d/xoyize.xyz.d/*.conf;
|
|||
|
|
|||
|
access_log /var/log/nginx/xoyize.xyz-access.log;
|
|||
|
error_log /var/log/nginx/xoyize.xyz-error.log;
|
|||
|
}
|
|||
|
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vérifier</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nginx -t
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Relancer</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl restart nginx
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Test redirection http/https avec curl depuis un poste distant</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl -I xoyize.xyz
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>HTTP/1.1 301 Moved Permanently
|
|||
|
Server: nginx/1.13.8
|
|||
|
Date: Sun, 11 Feb 2018 12:37:36 GMT
|
|||
|
Content-Type: text/html
|
|||
|
Content-Length: 185
|
|||
|
Connection: keep-alive
|
|||
|
Location: https:///
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Tester le lien <a href="https://xoyize.xyz">https://xoyize.xyz</a></p>
|
|||
|
|
|||
|
<p>Vérification headers <a href="https://securityheaders.io">https://securityheaders.io</a><br />
|
|||
|
Vérification complète <a href="https://observatory.mozilla.org/analyze.html?host=xoyize.xyz">https://observatory.mozilla.org/analyze.html?host=xoyize.xyz</a></p>
|
|||
|
|
|||
|
<h3 id="parefeu-iptables-v4-v6">parefeu (iptables V4 V6)</h3>
|
|||
|
|
|||
|
<p><img src="/images/iptables1.png" alt="parefeu Iptables" /></p>
|
|||
|
|
|||
|
<p>Créer un script pour mettre en place des règles de base.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd ~
|
|||
|
sudo nano parefeu
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/bin/sh
|
|||
|
|
|||
|
# Efface toutes les règles en cours. -F toutes. -X utilisateurs
|
|||
|
iptables -t filter -F
|
|||
|
iptables -t filter -X
|
|||
|
iptables -t nat -F
|
|||
|
iptables -t nat -X
|
|||
|
iptables -t mangle -F
|
|||
|
iptables -t mangle -X
|
|||
|
#
|
|||
|
ip6tables -t filter -F
|
|||
|
ip6tables -t filter -X
|
|||
|
|
|||
|
ip6tables -t mangle -F
|
|||
|
ip6tables -t mangle -X
|
|||
|
|
|||
|
# stratégie (-P) par défaut : bloc tout l'entrant le forward et autorise le sortant
|
|||
|
iptables -t filter -P INPUT DROP
|
|||
|
iptables -t filter -P FORWARD DROP
|
|||
|
iptables -t filter -P OUTPUT ACCEPT
|
|||
|
#
|
|||
|
ip6tables -t filter -P INPUT DROP
|
|||
|
ip6tables -t filter -P FORWARD DROP
|
|||
|
ip6tables -t filter -P OUTPUT ACCEPT
|
|||
|
|
|||
|
# Ne pas casser les connexions etablies
|
|||
|
iptables -t filter -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
|||
|
#
|
|||
|
ip6tables -t filter -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
|||
|
|
|||
|
# Autoriser Loopback
|
|||
|
iptables -t filter -A INPUT -i lo -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -o lo -j ACCEPT
|
|||
|
#
|
|||
|
ip6tables -t filter -A INPUT -i lo -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -o lo -j ACCEPT
|
|||
|
|
|||
|
# Autoriser icmp
|
|||
|
iptables -t filter -A INPUT -p icmp -j ACCEPT
|
|||
|
ip6tables -t filter -A INPUT -p icmp -j ACCEPT
|
|||
|
|
|||
|
# SSH
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 55026 -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 55026 -j ACCEPT
|
|||
|
#
|
|||
|
ip6tables -t filter -A INPUT -p tcp --dport 55026 -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p tcp --dport 55026 -j ACCEPT
|
|||
|
|
|||
|
# DNS in/out
|
|||
|
# /!\ Il faut autoriser le DNS AVANT de déclarer des hosts sinon pas de résolution de nom possible
|
|||
|
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
|
|||
|
#
|
|||
|
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
|
|||
|
|
|||
|
# HTTP + HTTPS
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 80 -j ACCEPT
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 443 -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 80 -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 443 -j ACCEPT
|
|||
|
|
|||
|
ip6tables -t filter -A INPUT -p tcp --dport 80 -j ACCEPT
|
|||
|
ip6tables -t filter -A INPUT -p tcp --dport 443 -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p tcp --dport 80 -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p tcp --dport 443 -j ACCEPT
|
|||
|
|
|||
|
# IMAP SMTP
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 993 -j ACCEPT
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 587 -j ACCEPT
|
|||
|
iptables -t filter -A INPUT -p tcp --dport 25 -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 993 -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 587 -j ACCEPT
|
|||
|
iptables -t filter -A OUTPUT -p tcp --dport 25 -j ACCEPT
|
|||
|
|
|||
|
ip6tables -t filter -A INPUT -p tcp --dport 993 -j ACCEPT
|
|||
|
ip6tables -t filter -A INPUT -p tcp --dport 587 -j ACCEPT
|
|||
|
ip6tables -t filter -A INPUT -p tcp --dport 25 -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p tcp --dport 993 -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p tcp --dport 587 -j ACCEPT
|
|||
|
ip6tables -t filter -A OUTPUT -p tcp --dport 25 -j ACCEPT
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>fichier exécutable :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo chmod +x parefeu
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vous pourrez le tester en l’exécutant directement en ligne de commande.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -s
|
|||
|
./parefeu
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>En cas d’erreur, redémarrez le serveur, les règles seront oubliées et vous permettront de reprendre la main.<br />
|
|||
|
En revanche, si les tests s’avèrent concluants, ajoutez le lancement des règles iptables au démarrage.<br />
|
|||
|
Après avoir exécuté le script précédent,vérifier la présence des règles</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -L
|
|||
|
ip6tables -L
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Lancement du parefeu au démarrage</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install iptables-persistent
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Faut-il enregistrer les règles IPv4 actuelles ? OUI<br />
|
|||
|
Faut-il enregistrer les règles IPv6 actuelles ? OUI<br />
|
|||
|
Les tables sont enregistrés sous <strong>/etc/iptables/</strong> , <strong>rules.v4</strong> pour IPv4 et <strong>rules.v6</strong> pour IPv6<br />
|
|||
|
Sauvegarde/Restauration manuelle des régles iptables</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
|
|||
|
iptables-restore < /etc/iptables/rules.v4
|
|||
|
ip6tables-restore < /etc/iptables/rules.v6
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>En cas de modification pouvez sauver les nouvelles règles du pare-feu</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dpkg-reconfigure iptables-persistent
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="sauvegarde">Sauvegarde</h3>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><a href="https://static.ouestline.net/linux/2017/03/29/sauvegarde-serveur-a-distance-via-rsync.html">Sauvegarde des serveurs distants via ssh et rsync </a></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>On trouvera dans le lien ci-dessus,tout le nécessaire pour installer les éléments pour un “serveur distant”<br />
|
|||
|
Le “serveur de sauvegarde” exécute une tâche planifiée<br />
|
|||
|
Les sauvegardes seront stockées sur le “serveur de sauvegarde”</p>
|
|||
|
|
|||
|
<h3 id="mises-à-jour-de-sécurité-debian">Mises à jour de sécurité Debian</h3>
|
|||
|
|
|||
|
<p>Rendre automatique les mises à jour de sécurité Debian en utilisant <strong>cron-apt</strong>.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install cron-apt
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Création du fichier security.sources.list utilisé par cron-apt et qui ne contiendra que le(s) dépôt(s) de sécurité Debian.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> grep security /etc/apt/sources.list > /etc/apt/security.sources.list
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>On édite le fichier de configuration de cron-apt sous <strong>/etc/cron-apt/config</strong> pour y ajouter les lignes suivantes.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>APTCOMMAND=/usr/bin/apt-get
|
|||
|
OPTIONS="-o quiet=1 -o Dir::Etc::SourceList=/etc/apt/security.sources.list"
|
|||
|
MAILTO="hostmaster@xoyize.xyz"
|
|||
|
MAILON="always"
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>Ne pas oublier de définir l’adresse de messagerie MAILTO</p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p>Par défaut, cron-apt télécharge les mise à jour mais ne les installe pas.<br />
|
|||
|
Pour déclencher la mise à jour automatique après téléchargement , modifier le fichier <strong>/etc/cron-apt/action.d/3-download</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/cron-apt/action.d/3-download
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>et supprimer le paramètre <strong>-d</strong> dans la ligne suivante.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dist-upgrade -y -o APT::Get::Show-Upgraded=true
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Lancer un test</p>
|
|||
|
|
|||
|
<p>cron-apt</p>
|
|||
|
|
|||
|
<p>Vous devriez recevoir un message de ce type</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>CRON-APT RUN [/etc/cron-apt/config]: Tue Feb 20 07:48:32 CET 2018
|
|||
|
CRON-APT ACTION: 0-update
|
|||
|
CRON-APT LINE: /usr/bin/apt-get -o quiet=1 -o Dir::Etc::SourceList=/etc/apt/security.sources.list update -o quiet=2
|
|||
|
CRON-APT ACTION: 3-download
|
|||
|
CRON-APT LINE: /usr/bin/apt-get -o quiet=1 -o Dir::Etc::SourceList=/etc/apt/security.sources.list autoclean -y
|
|||
|
Reading package lists...
|
|||
|
Building dependency tree...
|
|||
|
Reading state information...
|
|||
|
CRON-APT LINE: /usr/bin/apt-get -o quiet=1 -o Dir::Etc::SourceList=/etc/apt/security.sources.list dist-upgrade -y -o APT::Get::Show-Upgraded=true
|
|||
|
Reading package lists...
|
|||
|
Building dependency tree...
|
|||
|
Reading state information...
|
|||
|
Calculating upgrade...
|
|||
|
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Le programme utilise une tache cron pour se lancer automatiquement (par défaut , 4h00 du matin).<br />
|
|||
|
Pour spécifier le créneau horaire modifier le fichier <strong>/etc/cron.d/cron-apt</strong>.</p>
|
|||
|
|
|||
|
<h2 id="serveur-de-messagerie">Serveur de messagerie</h2>
|
|||
|
|
|||
|
<p><a href="https://mondedie.fr/d/5750-Tuto-Installer-un-serveur-de-mail-avec-Postfix-Dovecot-et-Rainloop">Installation sécurisée d’un serveur de mail avec Postfix, Dovecot et Rainloop</a><br />
|
|||
|
<a href="https://linode.com/docs/email/postfix/configure-spf-and-dkim-in-postfix-on-debian-8/">Configure SPF and DKIM With Postfix on Debian 8</a></p>
|
|||
|
|
|||
|
<p><img src="/images/serveur-messagerie.png" alt="Serveur de messagerie" /></p>
|
|||
|
|
|||
|
<h3 id="prérequis">Prérequis</h3>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Domaine : xoyize.xyz</li>
|
|||
|
<li>IPv4 du serveur : x.x.x.x</li>
|
|||
|
<li>IPv6 du serveur : xxxx:xxxx:xx:xxxx::1</li>
|
|||
|
<li>Courrier entrant : imap.xoyize.xyz sur port 993 (imaps)</li>
|
|||
|
<li>Courrier sortant : smtp.xoyize.xyz sur port 587 (submission)</li>
|
|||
|
<li>Compte courrier : yann@xoyize.xyz</li>
|
|||
|
<li>Certificats : Let’s Encrypt</li>
|
|||
|
<li>Serveur Debian Stretch + Nginx + PHP7 + MariaDb + accès SSH</li>
|
|||
|
<li>DNS de base configuré (OVH)</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Vérifier que les enregistrements MX de votre configuration DNS du domaine <strong>xoyize.xyz</strong> pointent vers votre serveur:</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dig +short MX xoyize.xyz
|
|||
|
10 xoyize.xyz.
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Les certificats letsencrypt</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/etc/ssl/private/xoyize.xyz-fullchain.pem
|
|||
|
/etc/ssl/private/xoyize.xyz-key.pem
|
|||
|
/etc/ssl/private/xoyize.xyz-chain.pem
|
|||
|
/etc/ssl/private/xoyize.xyz-ca.pem
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Toutes les opérations se font en mode su</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -s
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="postfix">Postfix</h3>
|
|||
|
|
|||
|
<p><img src="/images/postfix.png" alt="Postfix" /></p>
|
|||
|
|
|||
|
<p>On commence par installer Postfix avec le support de mysql. Les domaines, comptes utilisateurs et alias seront ainsi gérés directement au sein d’une base de données, que vous pourrez administrer grâce à vos outils habituels, comme phpMyAdmin par exemple.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install postfix postfix-mysql -y
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Postfix, compléter les options comme ceci :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Configuration type du serveur de messagerie : Site Internet
|
|||
|
Nom de courrier : xoyize.xyz
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Générer les paramètres DH :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>openssl dhparam -out /etc/postfix/dh_2048.pem -2 -rand /var/run/egd-pool 2048 # on peur coder 2048 au lieu de 1024
|
|||
|
openssl dhparam -out /etc/postfix/dh_512.pem -2 -rand /var/run/egd-pool 512
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Création d’une base postfix et d’un utilisateur postfix ayant tous les privilèges sur la base :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mysql -u root -p
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>La requête :</p>
|
|||
|
|
|||
|
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">DATABASE</span> <span class="k">postfix</span><span class="p">;</span>
|
|||
|
<span class="k">CREATE</span> <span class="k">USER</span> <span class="s1">'postfix'</span><span class="o">@</span><span class="s1">'localhost'</span> <span class="n">IDENTIFIED</span> <span class="k">BY</span> <span class="s1">'iq9awn3wEuUPxsi6qrdU'</span><span class="p">;</span>
|
|||
|
<span class="k">GRANT</span> <span class="k">ALL</span> <span class="k">PRIVILEGES</span> <span class="k">ON</span> <span class="nv">`postfix`</span><span class="p">.</span><span class="o">*</span> <span class="k">TO</span> <span class="s1">'postfix'</span><span class="o">@</span><span class="s1">'localhost'</span><span class="p">;</span>
|
|||
|
<span class="n">FLUSH</span> <span class="k">PRIVILEGES</span><span class="p">;</span>
|
|||
|
<span class="n">QUIT</span>
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h4 id="postfixadmin">PostfixAdmin</h4>
|
|||
|
|
|||
|
<p>Télécharger la dernière version sur le dossier /var/www</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd /var/www
|
|||
|
wget 'http://sourceforge.net/projects/postfixadmin/files/postfixadmin/postfixadmin-3.1/postfixadmin-3.1.tar.gz/download' -O pfa.tar.gz
|
|||
|
tar -zxvf pfa.tar.gz
|
|||
|
mv postfixadmin-3.1 pfa
|
|||
|
rm pfa.tar.gz
|
|||
|
chown -R www-data. pfa
|
|||
|
cd pfa
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Il faut également télécharger <strong>php imap</strong></p>
|
|||
|
|
|||
|
<p>apt install php7.0-imap -y</p>
|
|||
|
|
|||
|
<p>Vous n’avez plus qu’à créer le fichier <strong>config.local.php</strong> dans le répertoire de PostfixAdmin <strong>/var/www/pfa</strong> :</p>
|
|||
|
|
|||
|
<p>nano /var/www/pfa/config.local.php</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><?php
|
|||
|
$CONF['configured'] = true;
|
|||
|
$CONF['database_type'] = 'mysqli';
|
|||
|
$CONF['database_host'] = 'localhost';
|
|||
|
$CONF['database_user'] = 'postfix';
|
|||
|
$CONF['database_password'] = 'iq9awn3wEuUPxsi6qrdU';
|
|||
|
$CONF['database_name'] = 'postfix';
|
|||
|
$CONF['admin_email'] = 'admin@xoyize.xyz';
|
|||
|
$CONF['domain_path'] = 'YES';
|
|||
|
$CONF['domain_in_mailbox'] = 'NO';
|
|||
|
$CONF['fetchmail'] = 'NO';
|
|||
|
$CONF['encrypt'] = 'md5crypt';
|
|||
|
?>
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Les droits</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chown www-data.www-data /var/www/pfa/config.local.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour éviter l’erreur lors de l’accès à <strong>setup.php</strong> : <em>ERROR: the templates_c directory doesn’t exist or isn’t writeable for the webserver</em> :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir -p /var/www/pfa/templates_c
|
|||
|
chown -R www-data: /var/www/pfa/templates_c/
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Rendez-vous sur le <strong>setup.php</strong> de PostfixAdmin depuis votre navigateur <a href="https://xoyize.xyz/pfa/setup.php">https://xoyize.xyz/pfa/setup.php</a> <br />
|
|||
|
Une vérification est effectué et si tout est OK ,il faut créer un “Setup password” puis générer un hash bouton “generate password hash”<br />
|
|||
|
Editer le fichier le fichier <strong>config.local.php</strong> pour y ajouter le hash du “Setup password” :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$CONF['setup_password'] = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajouter un administrateur (Create superadmin account) nommé postfixadmin@xoyize.xyz<br />
|
|||
|
Le message suivant “You can now login to PostfixAdmin using the account you just created.” nous indique que lèon peut se connecter</p>
|
|||
|
|
|||
|
<p>Ouvrir l’interface d’administration de PostfixAdmin <a href="https://xoyize.xyz/pfa/login.php">https://xoyize.xyz/pfa/login.php</a> avec postfixadmin@xoyize.xyz , vous devez ajouter un nouveau domaine puis un nouveau compte courrier.</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Domaine : xoyize.xyz</li>
|
|||
|
<li>Compte courriel : yan@xoyize.xyz</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h4 id="configuration-de-postfix">Configuration de Postfix</h4>
|
|||
|
|
|||
|
<p>Maintenant nous allons devoir configurer Postfix pour qu’il prenne en charge correctement les connexions SMTP et l’envoie des messages sur le réseau pour chaque utilisateur créé via PostfixAdmin.
|
|||
|
Faites une sauvegarde du fichier de conf de Postfix <strong>/etc/postfix/main.cf</strong> :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cp /etc/postfix/main.cf /etc/postfix/main.cf.bak
|
|||
|
nano /etc/postfix/main.cf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Règles sur les adresses de destination
|
|||
|
# permit_sasl_authenticated : Accepter la connexion lorsque le client est authentifié
|
|||
|
# reject_non_fqdn_recipient : Refuser les adresses de destinations invalides (non FQDN)
|
|||
|
smtpd_recipient_restrictions =
|
|||
|
permit_mynetworks,
|
|||
|
permit_sasl_authenticated,
|
|||
|
reject_non_fqdn_recipient,
|
|||
|
reject_unauth_destination,
|
|||
|
reject_unknown_recipient_domain,
|
|||
|
reject_rbl_client zen.spamhaus.org
|
|||
|
|
|||
|
# Règles sur l'échange HELO qui survient avant la connexion
|
|||
|
# reject_invalid_helo_hostname : Refuser les échanges HELO invalides
|
|||
|
# reject_non_fqdn_helo_hostname : Refuser les noms d'hôte invalides (non FQDN)
|
|||
|
# reject_unknown_helo_hostname : Refuser les noms d'hôte qui n'ont pas de champ DNS A ou MX dans leurs DNS.
|
|||
|
smtpd_helo_restrictions =
|
|||
|
permit_mynetworks,
|
|||
|
permit_sasl_authenticated,
|
|||
|
reject_invalid_helo_hostname,
|
|||
|
|
|||
|
reject_non_fqdn_helo_hostname,
|
|||
|
# reject_unknown_helo_hostname
|
|||
|
|
|||
|
# Règles de connexion des clients
|
|||
|
# permit_sasl_authenticated : Accepter la connexion lorsque le client est authentifié
|
|||
|
# reject_plaintext_session : Refuser les connexions non sécurisées
|
|||
|
# reject_unauth_pipelining : Refuser les défauts lors de la connexion
|
|||
|
smtpd_client_restrictions =
|
|||
|
permit_mynetworks,
|
|||
|
permit_inet_interfaces,
|
|||
|
permit_sasl_authenticated,
|
|||
|
# reject_plaintext_session,
|
|||
|
# reject_unauth_pipelining
|
|||
|
|
|||
|
# Règles sur les expéditeurs
|
|||
|
# reject_non_fqdn_sender : Refuser les expéditeurs invalides (non FQDN)
|
|||
|
# reject_unknown_sender_domain : Refuser les expéditeurs qui n'ont pas de champ DNS A ou MX dans leurs DNS.
|
|||
|
# reject_sender_login_mismatch : Refuser les expéditeurs locaux non authentifiés
|
|||
|
smtpd_sender_restrictions =
|
|||
|
reject_non_fqdn_sender,
|
|||
|
|
|||
|
reject_unknown_sender_domain,
|
|||
|
reject_sender_login_mismatch
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ensuite nous allons définir les paramètres de chiffrement via TLS :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Smtp ( OUTGOING / Client )
|
|||
|
smtp_tls_loglevel = 1
|
|||
|
smtp_tls_security_level = may
|
|||
|
smtp_tls_CAfile = /etc/ssl/private/xoyize.xyz-ca.pem
|
|||
|
smtp_tls_protocols = !SSLv3
|
|||
|
smtp_tls_mandatory_protocols = !SSLv3
|
|||
|
smtp_tls_mandatory_ciphers = high
|
|||
|
smtp_tls_exclude_ciphers = aNULL, eNULL, EXPORT, DES, 3DES, RC2, RC4, MD5, PSK, SRP, DSS, AECDH, ADH
|
|||
|
smtp_tls_note_starttls_offer = yes
|
|||
|
|
|||
|
# ---------------------------------------------------------------------------------------------------
|
|||
|
|
|||
|
# Smtpd ( INCOMING / Server )
|
|||
|
smtpd_tls_loglevel = 1
|
|||
|
smtpd_tls_auth_only = yes
|
|||
|
smtpd_tls_security_level = may
|
|||
|
smtpd_tls_received_header = yes
|
|||
|
smtpd_tls_protocols = !SSLv3
|
|||
|
smtpd_tls_mandatory_protocols = !SSLv3
|
|||
|
smtpd_tls_mandatory_ciphers = medium
|
|||
|
|
|||
|
# Infos (voir : postconf -d)
|
|||
|
# Medium cipherlist = aNULL:-aNULL:ALL:!EXPORT:!LOW:+RC4:@STRENGTH
|
|||
|
# High cipherlist = aNULL:-aNULL:ALL:!EXPORT:!LOW:!MEDIUM:+RC4:@STRENGTH
|
|||
|
|
|||
|
# smtpd_tls_exclude_ciphers = NE PAS modifier cette directive pour des raisons de compatibilité
|
|||
|
# avec les autres serveurs de mail afin d'éviter une erreur du type
|
|||
|
# "no shared cipher" ou "no cipher overlap" puis un fallback en
|
|||
|
# plain/text...
|
|||
|
# smtpd_tls_cipherlist = Ne pas modifier non plus !
|
|||
|
|
|||
|
smtpd_tls_CAfile = $smtp_tls_CAfile
|
|||
|
smtpd_tls_cert_file = /etc/ssl/private/xoyize.xyz-chain.pem
|
|||
|
smtpd_tls_key_file = /etc/ssl/private/xoyize.xyz-key.pem
|
|||
|
smtpd_tls_dh1024_param_file = $config_directory/dh2048.pem
|
|||
|
smtpd_tls_dh512_param_file = $config_directory/dh512.pem
|
|||
|
|
|||
|
tls_preempt_cipherlist = yes
|
|||
|
tls_random_source = dev:/dev/urandom
|
|||
|
|
|||
|
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
|
|||
|
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
|
|||
|
lmtp_tls_session_cache_database = btree:${data_directory}/lmtp_scache
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ensuite nous allons définir les paramètres de connexion via SASL :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Paramètres de connexion SASL
|
|||
|
# C'est ici que l'on déclare Dovecot comme une passerelle pour authentifier les utilisateurs.
|
|||
|
# Postfix peut s'appuyer sur Dovecot pour identifier les connexions SMTP.
|
|||
|
smtpd_sasl_auth_enable = yes
|
|||
|
smtpd_sasl_type = dovecot
|
|||
|
smtpd_sasl_path = private/auth
|
|||
|
smtpd_sasl_security_options = noanonymous
|
|||
|
smtpd_sasl_tls_security_options = $smtpd_sasl_security_options
|
|||
|
smtpd_sasl_local_domain = $mydomain
|
|||
|
smtpd_sasl_authenticated_header = yes
|
|||
|
|
|||
|
broken_sasl_auth_clients = yes
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Maintenant on va s’occuper de la gestion et du stockage des emails. Dans la suite de ce tutoriel nous allons créer un utilisateur nommé vmail avec UID/GID de 5000, avec un HOME par défaut pointant sur <strong>/var/mail</strong>. Postfix doit le savoir donc on lui indique avec les 4 paramètres suivants :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virtual_uid_maps = static:5000
|
|||
|
virtual_gid_maps = static:5000
|
|||
|
virtual_minimum_uid = 5000
|
|||
|
virtual_mailbox_base = /var/mail
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Les 4 règles suivantes permettent à Postfix de savoir comment se connecter et lire la base de donnée afin de récupérer des informations sur les différents domaines, adresses virtuelles et alias. Nous allons détailler le contenu de ces 4 fichiers juste après.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
|
|||
|
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
|
|||
|
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf
|
|||
|
|
|||
|
smtpd_sender_login_maps = mysql:/etc/postfix/mysql-sender-login-maps.cf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Le paramètre <strong>virtual_transport</strong> est très très important, il permet à Postfix de savoir où envoyer les emails reçus. Dans notre cas, on utilise le protocole <strong>LMTP</strong> pour les acheminer jusqu’à Dovecot :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virtual_transport = lmtp:unix:private/dovecot-lmtp
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Il ne reste plus qu’à definir les paramètres généraux. N’oubliez pas de modifier la valeur de <strong>myhostname</strong> et <strong>myorigin</strong> en indiquant votre <strong>FQDN</strong>.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
|
|||
|
biff = no
|
|||
|
append_dot_mydomain = no
|
|||
|
readme_directory = no
|
|||
|
delay_warning_time = 4h
|
|||
|
mailbox_command = procmail -a "$EXTENSION"
|
|||
|
recipient_delimiter = +
|
|||
|
disable_vrfy_command = yes
|
|||
|
message_size_limit = 502400000
|
|||
|
mailbox_size_limit = 1024000000
|
|||
|
|
|||
|
inet_interfaces = all
|
|||
|
inet_protocols = ipv4
|
|||
|
|
|||
|
myhostname = xoyize.xyz
|
|||
|
myorigin = xoyize.xyz
|
|||
|
mydestination = localhost localhost.$mydomain
|
|||
|
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
|
|||
|
relayhost =
|
|||
|
|
|||
|
alias_maps = hash:/etc/aliases
|
|||
|
alias_database = hash:/etc/aliases
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>C’est terminé pour le fichier de configuration principale, je vous l’accorde il y a pas mal de paramètres à prendre en compte mais dans le cas d’un serveur SMTP c’est pas étonnant. <br />
|
|||
|
Vous pouvez aller voir la documentation de Postfix si vous voulez avoir plus d’informations sur sa configuration. http://www.postfix.org/postconf.5.html<br />
|
|||
|
Fichier de configuration complet <strong>main.cf</strong> : https://gist.github.com/hardware/b26918353c6325c09310</p>
|
|||
|
|
|||
|
<h4 id="configuration-de-postfix-pour-mysqlmariadb">Configuration de Postfix pour MySQL/MariaDb</h4>
|
|||
|
|
|||
|
<p>Créer les 4 fichiers de configuration qui vont permettre à Postfix d’interagir avec MySQL :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/postfix/mysql-virtual-mailbox-domains.cf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hosts = 127.0.0.1
|
|||
|
user = postfix
|
|||
|
password = iq9awn3wEuUPxsi6qrdU
|
|||
|
dbname = postfix
|
|||
|
|
|||
|
query = SELECT domain FROM domain WHERE domain='%s' and backupmx = 0 and active = 1
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/postfix/mysql-virtual-mailbox-maps.cf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hosts = 127.0.0.1
|
|||
|
user = postfix
|
|||
|
password = iq9awn3wEuUPxsi6qrdU
|
|||
|
dbname = postfix
|
|||
|
|
|||
|
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = 1
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/postfix/mysql-virtual-alias-maps.cf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hosts = 127.0.0.1
|
|||
|
user = postfix
|
|||
|
password = iq9awn3wEuUPxsi6qrdU
|
|||
|
dbname = postfix
|
|||
|
|
|||
|
query = SELECT goto FROM alias WHERE address='%s' AND active = 1
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/postfix/mysql-sender-login-maps.cf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hosts = 127.0.0.1
|
|||
|
user = postfix
|
|||
|
password = iq9awn3wEuUPxsi6qrdU
|
|||
|
dbname = postfix
|
|||
|
|
|||
|
query = SELECT username FROM mailbox WHERE username='%s' AND active = 1
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Si vous voulez activer le port 587 pour vous connecter de manière sécurisé par SMTPS avec n’importe quel client mail, il faut décommenter/modifier les lignes suivantes dans le fichier <strong>/etc/postfix/master.cf</strong> :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>submission inet n - - - - smtpd
|
|||
|
-o syslog_name=postfix/submission
|
|||
|
-o smtpd_tls_dh1024_param_file=/etc/postfix/dh_2048.pem
|
|||
|
-o smtpd_tls_security_level=encrypt
|
|||
|
-o smtpd_sasl_auth_enable=yes
|
|||
|
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Et n’oubliez pas de décommentez cette ligne aussi si ce n’est pas déjà fait :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>smtp inet n - - - - smtpd
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Fichier de configuration complet <strong>master.cf</strong> : https://gist.github.com/hardware/dcc120df9329da7f40ec</p>
|
|||
|
|
|||
|
<h3 id="dovecot">Dovecot</h3>
|
|||
|
|
|||
|
<p><img src="/images/dovecot.png" alt="Dovecot" /></p>
|
|||
|
|
|||
|
<p>Liens utiles :<br />
|
|||
|
http://fr.wikipedia.org/wiki/Dovecot<br />
|
|||
|
http://wiki2.dovecot.org/</p>
|
|||
|
|
|||
|
<p><em>Dovecot est un serveur IMAP et POP3 pour les systèmes d’exploitation Unix et dérivés, conçu avec comme premier but la sécurité. Dovecot est distribué en double licence MIT et GPL version 2.</em></p>
|
|||
|
|
|||
|
<p>Installation des paquets :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install dovecot-core dovecot-imapd dovecot-lmtpd dovecot-mysql -y
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h4 id="configuration-de-dovecot">Configuration de dovecot</h4>
|
|||
|
|
|||
|
<p>Ajoutez la liste des protocoles activés après l’instruction “!include_try” dans le fichier <strong>/etc/dovecot/dovecot.conf</strong>. Dans notre cas, nous allons activer IMAP pour la récupération des emails via le port 993 et LMTP pour l’acheminement des emails entre Postfix et Dovecot :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/dovecot/dovecot.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>!include_try /usr/share/dovecot/protocols.d/*.protocol
|
|||
|
protocols = imap lmtp
|
|||
|
listen = *
|
|||
|
|
|||
|
# Assurez-vous que cette ligne est bien décommentée :
|
|||
|
!include conf.d/*.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Fichier de configuration complet <strong>dovecot.conf</strong> : https://gist.github.com/hardware/ce55b0d241e229d5176a</p>
|
|||
|
|
|||
|
<p>On indique le chemin du conteneur local qui contiendra tous nos emails. Editez le fichier <strong>10-mail.conf</strong> :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/dovecot/conf.d/10-mail.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Le contenur local est organisé de cette manière :
|
|||
|
# /var/mail/vhosts/xoyize.xyz/utilisateur
|
|||
|
mail_location = maildir:/var/mail/vhosts/%d/%n/mail
|
|||
|
maildir_stat_dirs=yes
|
|||
|
|
|||
|
namespace inbox {
|
|||
|
inbox = yes
|
|||
|
}
|
|||
|
|
|||
|
mail_uid = 5000
|
|||
|
mail_gid = 5000
|
|||
|
|
|||
|
first_valid_uid = 5000
|
|||
|
last_valid_uid = 5000
|
|||
|
|
|||
|
mail_privileged_group = vmail
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Fichier de configuration complet <strong>10-mail.conf</strong> : https://gist.github.com/hardware/0fd157d8e83e09b50cb9</p>
|
|||
|
|
|||
|
<p>Les emails seront stockés dans le répertoire <strong>/var/mail</strong>. On doit donc créer un répertoire correspondant à notre domaine (celui qui est présent dans la table “domain”).</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir -p /var/mail/vhosts/xoyize.xyz
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Maintenant on ajoute un nouvel utilisateur et un nouveau groupe nommé <strong>vmail</strong> avec un UID/GID de 5000 :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>groupadd -g 5000 vmail
|
|||
|
useradd -g vmail -u 5000 vmail -d /var/mail
|
|||
|
chown -R vmail:vmail /var/mail
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Editer le fichier <strong>10-auth.conf</strong> et modifier les lignes suivantes :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/dovecot/conf.d/10-auth.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>disable_plaintext_auth = yes
|
|||
|
auth_mechanisms = plain login
|
|||
|
#!include auth-system.conf.ext # Commenter cette ligne
|
|||
|
!include auth-sql.conf.ext # décommenter cette ligne
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Fichier de configuration complet <strong>10-auth.conf</strong> : https://gist.github.com/hardware/3a70923d37e6ad7c1859</p>
|
|||
|
|
|||
|
<p>Maintenant on va définir deux méthodes qui vont permettrent à Dovecot de savoir comment obtenir les utilisateurs et les mots de passe correspondants lors de la connexion. Editez le fichier <strong>auth-sql.conf.ext</strong> :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/dovecot/conf.d/auth-sql.conf.ext
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Le mot de passe est obtenu à partir de la base de donnée
|
|||
|
passdb {
|
|||
|
driver = sql
|
|||
|
args = /etc/dovecot/dovecot-sql.conf.ext
|
|||
|
}
|
|||
|
|
|||
|
# Par contre le nom d'utilisateur est obtenu de manière statique à partir du conteneur local
|
|||
|
# %d = domaine.tld
|
|||
|
# %n = utilisateur
|
|||
|
userdb {
|
|||
|
driver = static
|
|||
|
args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n
|
|||
|
}
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ensuite editez le fichier <strong>dovecot-sql.conf.ext</strong> et modifiez les paramètres suivants :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano/etc/dovecot/dovecot-sql.conf.ext
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Paramètres de connexion
|
|||
|
driver = mysql
|
|||
|
connect = host=127.0.0.1 dbname=postfix user=postfix password=iq9awn3wEuUPxsi6qrdU
|
|||
|
|
|||
|
# Permet de définir l'algorithme de hachage.
|
|||
|
# Pour plus d'information: http://wiki2.dovecot.org/Authentication/PasswordSchemes
|
|||
|
# /!\ ATTENTION : ne pas oublier de modifier le paramètre "$CONF['encrypt'] = 'md5crypt';" de PostfixAdmin
|
|||
|
default_pass_scheme = MD5-CRYPT
|
|||
|
|
|||
|
# Requête de récupération du mot de passe du compte utilisateur
|
|||
|
password_query = SELECT password FROM mailbox WHERE username = '%u'
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Modifiez les permissions sur le répertoire <strong>/etc/dovecot</strong> :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chown -R vmail:dovecot /etc/dovecot
|
|||
|
chmod -R o-rwx /etc/dovecot
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Editer le fichier <strong>10-master.conf</strong> avec le contenu suivant :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/dovecot/conf.d/10-master.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>service imap-login {
|
|||
|
|
|||
|
inet_listener imap {
|
|||
|
port = 143
|
|||
|
}
|
|||
|
|
|||
|
inet_listener imaps {
|
|||
|
port = 993
|
|||
|
ssl = yes
|
|||
|
}
|
|||
|
|
|||
|
service_count = 0
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
service imap {
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
service lmtp {
|
|||
|
|
|||
|
# On autorise Postfix à transférer les emails dans le spooler de Dovecot via LMTP
|
|||
|
unix_listener /var/spool/postfix/private/dovecot-lmtp {
|
|||
|
mode = 0600
|
|||
|
user = postfix
|
|||
|
group = postfix
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
service auth {
|
|||
|
|
|||
|
# On autorise Postfix à se connecter à Dovecot via LMTP
|
|||
|
unix_listener /var/spool/postfix/private/auth {
|
|||
|
mode = 0666
|
|||
|
user = postfix
|
|||
|
group = postfix
|
|||
|
}
|
|||
|
|
|||
|
# On indique à Dovecot les permissions du conteneur local
|
|||
|
unix_listener auth-userdb {
|
|||
|
mode = 0600
|
|||
|
user = vmail
|
|||
|
group = vmail
|
|||
|
}
|
|||
|
|
|||
|
user = dovecot
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
service auth-worker {
|
|||
|
|
|||
|
user = vmail
|
|||
|
|
|||
|
}
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Fichier de configuration complet <strong>dovecot-sql.conf.ext</strong> : https://gist.github.com/hardware/cdc1bae0a7e306501d15</p>
|
|||
|
|
|||
|
<p>Enfin, editez le fichier <strong>10-ssl.conf</strong> et modifiez les paramètres suivants :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/dovecot/conf.d/10-ssl.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssl = required
|
|||
|
ssl_cert = </etc/ssl/private/xoyize.xyz-chain.pem
|
|||
|
ssl_key = </etc/ssl/private/xoyize.xyz-key.pem
|
|||
|
ssl_protocols = !SSLv2 !SSLv3
|
|||
|
#ssl_protocols = !SSLv3
|
|||
|
ssl_cipher_list = ALL:!aNULL:!eNULL:!LOW:!MEDIUM:!EXP:!RC2:!RC4:!DES:!3DES:!MD5:!PSK:!SRP:!DSS:!AECDH:!ADH:@STRENGTH
|
|||
|
ssl_prefer_server_ciphers = yes # Dovecot > 2.2.x
|
|||
|
ssl_dh_parameters_length = 4096 # Dovecot > 2.2.x
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Fichier de configuration complet <strong>10-ssl.conf</strong> : https://gist.github.com/hardware/91ed5e01929b8b414ae0</p>
|
|||
|
|
|||
|
<h3 id="redémarrage-des-services-et-vérification-des-ports">Redémarrage des services et vérification des ports</h3>
|
|||
|
|
|||
|
<p>On redémarre Postfix et Dovecot :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl restart postfix dovecot
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Puis on vérifie que les ports 25 (SMTP), 587 (SMTPS) et 993 (IMAPS) sont bien en écoute sur 0.0.0.0. N’oubliez pas de les ouvrir au niveau de votre Firewall :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>netstat -ptna
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Proto Recv-Q Send-Q Adresse locale Adresse distante Etat PID/Program name
|
|||
|
tcp 0 0 0.0.0.0:25 0.0.0.0:* LISTEN 6116/master
|
|||
|
tcp 0 0 0.0.0.0:993 0.0.0.0:* LISTEN 6009/dovecot
|
|||
|
tcp 0 0 0.0.0.0:587 0.0.0.0:* LISTEN 6116/master
|
|||
|
tcp 0 0 0.0.0.0:143 0.0.0.0:* LISTEN 6009/dovecot
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vous pouvez tester le serveur Postfix localement à partir du terminal en utilisant une connexion directe avec netcat, telnet ou similaire. Les commandes suivantes doivent être exécutées dans l’ordre correct pour que le serveur ferme la connexion.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nc xoyize.xyz 25
|
|||
|
EHLO $hostname
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>250-xoyize.xyz
|
|||
|
250-PIPELINING
|
|||
|
250-SIZE 502400000
|
|||
|
250-ETRN
|
|||
|
250-STARTTLS
|
|||
|
250-ENHANCEDSTATUSCODES
|
|||
|
250-8BITMIME
|
|||
|
250 DSN
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Si vous voyez 250-STARTTLS, c’est que le serveur supporte bien l’authentification par STARTTLS.</p>
|
|||
|
|
|||
|
<h3 id="dkimspf-et-dmarc">Dkim,Spf et Dmarc</h3>
|
|||
|
|
|||
|
<h4 id="opendkim">OpenDKIM</h4>
|
|||
|
|
|||
|
<p><img src="/images/dkim.png" alt="OpenDkim" /></p>
|
|||
|
|
|||
|
<p>DKIM est un standard permettant d’associer de manière forte une entité ou une organisation avec un domaine au sein d’un email. On se déclare donc complètement responsable de la transmission du message sur le réseau. DKIM fonctionne par cryptographie asymétrique, le MTA (Mail Transfer Agent, dans notre cas il s’agit de Postfix) se charge de signer numériquement tous les emails envoyés avec une clé privée contenue sur le serveur. Le destinataire peut alors vérifier l’intégrité du corps ainsi que les en-têtes du message grâce à la clé publique fournie par le domainkey, qui est un champ TXT contenu dans les fichiers de zone de vos DNS.</p>
|
|||
|
|
|||
|
<p>Liens utiles :<br />
|
|||
|
http://fr.wikipedia.org/wiki/DomainKeys_Identified_Mail<br />
|
|||
|
http://www.opendkim.org/docs.html</p>
|
|||
|
|
|||
|
<p>On commence par installer OpenDKIM :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt-get install opendkim opendkim-tools
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Editez le fichier de configuration opendkim.conf avec le contenu suivant :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/opendkim.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>AutoRestart Yes
|
|||
|
AutoRestartRate 10/1h
|
|||
|
UMask 002
|
|||
|
Syslog Yes
|
|||
|
SyslogSuccess Yes
|
|||
|
LogWhy Yes
|
|||
|
|
|||
|
OversignHeaders From
|
|||
|
AlwaysAddARHeader Yes
|
|||
|
Canonicalization relaxed/simple
|
|||
|
|
|||
|
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
|
|||
|
InternalHosts refile:/etc/opendkim/TrustedHosts
|
|||
|
KeyTable refile:/etc/opendkim/KeyTable
|
|||
|
SigningTable refile:/etc/opendkim/SigningTable
|
|||
|
|
|||
|
Mode sv
|
|||
|
PidFile /var/run/opendkim/opendkim.pid
|
|||
|
SignatureAlgorithm rsa-sha256
|
|||
|
|
|||
|
UserID opendkim:opendkim
|
|||
|
|
|||
|
Socket local:/var/spool/postfix/opendkim/opendkim.sock
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour que Postfix puisse communiquer avec OpenDKIM, on va utiliser un socket, il faut créer le répertoire <strong>/var/spool/postfix/opendkim</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir /var/spool/postfix/opendkim
|
|||
|
chown opendkim: /var/spool/postfix/opendkim
|
|||
|
usermod -aG opendkim postfix
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ensuite on doit indiquer à Postfix comment s’interfacer avec OpenDKIM. Pour cela, editer le fichier <strong>main.cf</strong> et ajouter les lignes suivantes à fin du fichier de configuration :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/postfix/main.cf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>milter_protocol = 6
|
|||
|
milter_default_action = accept
|
|||
|
smtpd_milters = unix:/var/spool/postfix/opendkim/opendkim.sock
|
|||
|
non_smtpd_milters = unix:/var/spool/postfix/opendkim/opendkim.sock
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Créér le répertoire suivant :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir -p /etc/opendkim/keys
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajoutez le fichier TrustedHosts avec le contenu suivant :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/opendkim/TrustedHosts
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>127.0.0.1
|
|||
|
localhost
|
|||
|
::1
|
|||
|
*.xoyize.xyz
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>N’oubliez pas de modifier <strong>xoyize.xyz</strong> par votre nom de domaine.</p>
|
|||
|
|
|||
|
<p>Ajoutez le fichier <strong>KeyTable</strong> avec le contenu suivant. Dans cet exemple, nous utiliserons un sélecteur nommé <strong>mail</strong>. Vous pouvez le nommer comme vous le souhaitez.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/opendkim/KeyTable
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mail._domainkey.xoyize.xyz xoyize.xyz:mail:/etc/opendkim/keys/xoyize.xyz/mail.private
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajoutez le fichier <strong>SigningTable</strong> avec le contenu suivant :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/opendkim/SigningTable
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>*@xoyize.xyz mail._domainkey.xoyize.xyz
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Création de la clé publique et privée</p>
|
|||
|
|
|||
|
<p>Création du répertoire qui contiendra les clés :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd /etc/opendkim/keys
|
|||
|
mkdir xoyize.xyz && cd xoyize.xyz
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Génération des clés de chiffrement :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Paramètres : -s [sélecteur] -d [nom de domaine] -b [taille de la clé]
|
|||
|
opendkim-genkey -s mail -d xoyize.xyz -b 4096
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>Certains registrars n’acceptent pas les clés > 1024 bits donc pensez à changer le paramètre au-dessus si besoin.</p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p>Modification des permissions de la clé privée :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chown opendkim:opendkim mail.private
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Les permissions doivent être les suivantes (chmod 400) :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ls -lX /etc/opendkim/keys/xoyize.xyz/
|
|||
|
|
|||
|
-rw------- 1 opendkim opendkim 3243 févr. 14 21:05 mail.private
|
|||
|
-rw------- 1 root root 850 févr. 14 21:05 mail.txt
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Modification des DNS : Ajout de la clé publique</p>
|
|||
|
|
|||
|
<p>Copiez le contenu du fichier mail.txt (la clé publique) :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cat mail.txt
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Le format de l’enregistrement DKIM est comme ceci :</p>
|
|||
|
|
|||
|
<p><code class="language-plaintext highlighter-rouge">mail._domainkey IN TXT "v=DKIM1; k=rsa; p=VOTRE CLE PUBLIQUE"</code></p>
|
|||
|
|
|||
|
<p>La clé de 4096 bits :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cat mail.txt
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mail._domainkey IN TXT ( "v=DKIM1; k=rsa; "
|
|||
|
"p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5tSGc+j5pLEuWeR4NIhDRPhwHUZll1/N3ajHF1wSttUVp/igdcpZTKTD7ccFmsY0l+72Q+rlnoCGGdhdw0ImRoPVZwBfSxvC3alw/YLLCEwpy+J/tXx2WOPdHM95TVbt0S/wVWpPxmr3mAIPO24R4NEf0yQdvktnB26UTcLBEMtqAEtEDLqrea/XM5HQMngWXAARy379H6mo1Y"
|
|||
|
"ee3wUqYZPX+g8ljZZMpIlvaE0pjtVIZ0kV8/kXz0fa1XHanc6rTsF7/XxCLYJRAK6fcfO8u/Ro65uWNM3x8+WN1nkf/ojh8VN/0A5oUrlQVWba4OEXxCkbZTU7V+4okyyhAHV2+/c0qRsKRNj2YuPl3DmJ8/me/UBHbIEeji7kcBuVkgv0cakHolMbKWESBuuw1F5MPThQ3qbUcCI9mY4OtTnElk40DVXMenKwQ1EnqJZlHtj6XOo0/na4"
|
|||
|
"epqH+rtUOXhan6ewd8XQvJk630Qi1FHzWmO7i4pb8Uo0EDrAQmPvCNsR+AttrIdK7Ry3xgKAUdSyfT2GXmtP3hpr4l2w0Qv0sXAkVzz9xF0OsUjb3pztdAKz9t9pHiIj3C5p0HQZTAr7oVAzjiDu6gntfoMzTB6mY6PKF2TYOBagu0AZAwgwuWb24zrGPXRPS8U01TlY6UVzFJbB+97i0Nim5jQmyf/r38cCAwEAAQ==" ) ; ----- DKIM key mail for domaine.tld
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour OVH il faut supprimer les guillements intermédiaires</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mail._domainkey IN TXT ( "v=DKIM1; k=rsa; p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5tSGc+j5pLEuWeR4NIhDRPhwHUZll1/N3ajHF1wSttUVp/igdcpZTKTD7ccFmsY0l+72Q+rlnoCGGdhdw0ImRoPVZwBfSxvC3alw/YLLCEwpy+J/tXx2WOPdHM95TVbt0S/wVWpPxmr3mAIPO24R4NEf0yQdvktnB26UTcLBEMtqAEtEDLqrea/XM5HQMngWXAARy379H6mo1Yee3wUqYZPX+g8ljZZMpIlvaE0pjtVIZ0kV8/kXz0fa1XHanc6rTsF7/XxCLYJRAK6fcfO8u/Ro65uWNM3x8+WN1nkf/ojh8VN/0A5oUrlQVWba4OEXxCkbZTU7V+4okyyhAHV2+/c0qRsKRNj2YuPl3DmJ8/me/UBHbIEeji7kcBuVkgv0cakHolMbKWESBuuw1F5MPThQ3qbUcCI9mY4OtTnElk40DVXMenKwQ1EnqJZlHtj6XOo0/na4epqH+rtUOXhan6ewd8XQvJk630Qi1FHzWmO7i4pb8Uo0EDrAQmPvCNsR+AttrIdK7Ry3xgKAUdSyfT2GXmtP3hpr4l2w0Qv0sXAkVzz9xF0OsUjb3pztdAKz9t9pHiIj3C5p0HQZTAr7oVAzjiDu6gntfoMzTB6mY6PKF2TYOBagu0AZAwgwuWb24zrGPXRPS8U01TlY6UVzFJbB+97i0Nim5jQmyf/r38cCAwEAAQ==" ) ; ----- DKIM key mail for domaine.tld
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>On redémarre tous les services :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl restart postfix dovecot opendkim
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Les services démarrent automatiquement lors du boot (service enable lors de l’installation du paquet sur debian 9) :</p>
|
|||
|
|
|||
|
<p>Attendez quelques minutes la propagation des DNS. Vous pouvez vérifier que le champ <strong>domainkey</strong> est bien visible en exécutant la commande suivante à partir de votre pc :</p>
|
|||
|
|
|||
|
<p><code class="language-plaintext highlighter-rouge">host -t TXT mail._domainkey.xoyize.xyz</code></p>
|
|||
|
|
|||
|
<p>Si elle retourne ce message :</p>
|
|||
|
|
|||
|
<p><code class="language-plaintext highlighter-rouge">Host mail._domainkey.xoyize.xyz not found: (NXDOMAIN)</code></p>
|
|||
|
|
|||
|
<p>C’est que vos DNS ne se sont pas encore propagés sur les serveurs de votre FAI, patientez encore un peu.</p>
|
|||
|
|
|||
|
<h4 id="spf-sender-policy-framework">SPF (Sender Policy Framework)</h4>
|
|||
|
|
|||
|
<p><em>Sender Policy Framework (SPF) est une norme de vérification du nom de domaine de l’expéditeur d’un courrier électronique, normalisé dans la RFC 7208. L’adoption de cette norme est de nature à réduire le spam.</em>(Wikipédia)</p>
|
|||
|
|
|||
|
<p>Ajoutez cette ligne dans le fichier de zone de votre nom de domaine :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@ IN TXT "v=spf1 a mx ip4:ADRESSE IP DE VOTRE SERVEUR ~all"
|
|||
|
# OVH
|
|||
|
600 IN TXT "v=spf1 a mx ip4:93.115.96.97 ip6:2a03:75c0:35:670d::1 ~all"
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h4 id="dmarc">DMARC</h4>
|
|||
|
|
|||
|
<p><img src="/images/dmarc-logo1.png" alt="Dmarc" /></p>
|
|||
|
|
|||
|
<p><em>DMARC (Domain-based Message Authentication, Reporting & Conformance) est une spécification technique assez récente (2011), qui est toujours en draft par l’IETF ( https://datatracker.ietf.org/doc/rfc7489/ ), les contributeurs initiaux sont un consortium de plusieurs géants d’internet comme AOL, Yahoo, Google, Microsoft, Paypal, Facebook…etc. Cette spécification permet de réduire l’usage abusif des e-mails, tels que le spam, le phishing en ajoutant une couche supplémentaire dans le processus d’authentification des mails.</em></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install opendmarc
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Editer le fichier de configuration opendmarc.conf avec le contenu suivant :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/opendmarc.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>AutoRestart Yes
|
|||
|
AutoRestartRate 10/1h
|
|||
|
UMask 0002
|
|||
|
Syslog true
|
|||
|
|
|||
|
AuthservID "xoyize.xyz"
|
|||
|
TrustedAuthservIDs "xoyize.xyz"
|
|||
|
IgnoreHosts /etc/opendkim/TrustedHosts
|
|||
|
IgnoreMailFrom "xoyize.xyz"
|
|||
|
RejectFailures false
|
|||
|
|
|||
|
UserID opendmarc:opendmarc
|
|||
|
PidFile /var/run/opendmarc/opendmarc.pid
|
|||
|
Socket local:/var/spool/postfix/opendmarc/opendmarc.sock
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour que Postfix puisse communiquer avec Opendmarc, on va utiliser un socket, il faut créer le répertoire <strong>/var/spool/postfix/opendmarc</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir /var/spool/postfix/opendmarc
|
|||
|
chown opendmarc: /var/spool/postfix/opendmarc
|
|||
|
usermod -aG opendmarc postfix
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ensuite dans le fichier <strong>/etc/postfix/main.cf</strong>, editer cette ligne :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>smtpd_milters = unix:/var/spool/postfix/opendkim/opendkim.sock, unix:/var/spool/postfix/opendmarc/opendmarc.sock
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Puis on redémarre les services :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl restart opendmarc postfix
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour tester le bon fonctionnement du serveur de mail, utilisez le site http://www.mail-tester.com/</p>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>ATTENTION, le paramètre <strong>Socket</strong> n’est pas pris en compte , il faut paraméter <strong>smtpd_milters</strong> avec <strong>unix:/var/run/opendmarc/opendmarc.sock</strong><br />
|
|||
|
Il faut activer le service <code class="language-plaintext highlighter-rouge">systemctl enable opendmarc</code></p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<h3 id="spamassassin-anti-spam">SpamAssassin (Anti SPAM)</h3>
|
|||
|
|
|||
|
<p><img src="/images/spamassassin1.png" alt="SpamAssassin" /></p>
|
|||
|
|
|||
|
<p><em>SpamAssassin est un logiciel libre permettant de filtrer les emails afin d’éradiquer au maximum le SPAM. Il fait passer un certain nombre de tests au message et en fonction du résultat de ces tests, il attribue un score qui permettra de savoir si il s’agit d’un email indésirable ou non.</em></p>
|
|||
|
|
|||
|
<p>Liens utiles :<br />
|
|||
|
http://spamassassin.apache.org/<br />
|
|||
|
http://fr.wikipedia.org/wiki/SpamAssassin</p>
|
|||
|
|
|||
|
<p>Installer SpamAssassin :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install spamassassin spamc
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ensuite on modifie le fichier de configuration de Postfix <strong>/etc/postfix/master.cf</strong> :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>smtp inet n - - - - smtpd
|
|||
|
-o content_filter=spamassassin
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajoutez aussi ces deux lignes à la fin du fichier :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>spamassassin unix - n n - - pipe
|
|||
|
user=debian-spamd argv=/usr/bin/spamc -f -e /usr/sbin/sendmail -oi -f ${sender} ${recipient}
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ok maintenant on relance postfix avec la commande suivante :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl restart postfix
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Dans le fichier <strong>/etc/spamassassin/local.cf</strong>, décommenter la ligne suivante :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rewrite_header Subject *****SPAM*****
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Les mails considérés comme du SPAM auront un sujet préfixé avec <strong>SPAM</strong>. Ils pourront ainsi être traités comme vous le souhaitez en paramétrant un filtre dans votre client mail.</p>
|
|||
|
|
|||
|
<p>Ajouter aussi à la fin du fichier local.cf :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>report_safe 0
|
|||
|
whitelist_auth *@xoyize.xyz
|
|||
|
|
|||
|
add_header all Report _REPORT_
|
|||
|
add_header spam Flag _YESNOCAPS_
|
|||
|
add_header all Status _YESNO_, score=_SCORE_ required=_REQD_ tests=_TESTS_ autolearn=_AUTOLEARN_ version=_VERSION_
|
|||
|
add_header all Level _STARS(*)_
|
|||
|
add_header all Checker-Version SpamAssassin _VERSION_ (_SUBVERSION_) on _HOSTNAME_
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour avoir un rapport détaillé dans les headers de tous les mails, comme ceci :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>X-Spam-Report:
|
|||
|
* -5.0 RCVD_IN_DNSWL_HI RBL: Sender listed at http://www.dnswl.org/, high
|
|||
|
* trust
|
|||
|
* [66.45.63.27 listed in list.dnswl.org]
|
|||
|
* -3.0 RCVD_IN_RP_CERTIFIED RBL: Sender in ReturnPath Certified - Contact
|
|||
|
* cert-sa@returnpath.net
|
|||
|
* [Return Path SenderScore Certified {formerly]
|
|||
|
[Bonded Sender} - <http://www.senderscorecertified.com>]
|
|||
|
* -2.0 RCVD_IN_RP_SAFE RBL: Sender in ReturnPath Safe - Contact
|
|||
|
* safe-sa@returnpath.net
|
|||
|
* [Return Path SenderScore Safe List (formerly]
|
|||
|
[Habeas Safelist) - <http://www.senderscorecertified.com>]
|
|||
|
X-Spam-Status: No, score=-10.0 required=5.0 tests=RCVD_IN_DNSWL_HI,
|
|||
|
RCVD_IN_RP_CERTIFIED,RCVD_IN_RP_SAFE autolearn=ham
|
|||
|
version=3.3.2
|
|||
|
X-Spam-Level:
|
|||
|
X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on hostname.domain.tld
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>L’exemple ci-dessus montre un très bon élève (returnpath.net) mais ça sera pas toujours le cas avec les mails que vous recevrez</p>
|
|||
|
|
|||
|
<p>Dans le fichier <strong>/etc/default/spamassassin</strong>, modifier ces deux lignes :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># If you're using systemd (default for jessie), the ENABLED setting is
|
|||
|
# not used. Instead, enable spamd by issuing:
|
|||
|
# systemctl enable spamassassin.service
|
|||
|
# Change to "1" to enable spamd on systems using sysvinit:
|
|||
|
ENABLED=0
|
|||
|
# Cronjob
|
|||
|
# Set to anything but 0 to enable the cron job to automatically update
|
|||
|
# spamassassin's rules on a nightly basis
|
|||
|
CRON=0
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>On active le service lors du boot puis on le démarre :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl enable spamassassin.service
|
|||
|
systemctl start spamassassin.service
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour finir, ajouter au cron ces 3 lignes avec contab :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>crontab -e
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Mise à jour des règles de spamassassin
|
|||
|
20 02 * * * /usr/bin/sa-update
|
|||
|
|
|||
|
# Auto-apprentissage de spam assassin avec utilisateur yan
|
|||
|
30 02 * * * /usr/bin/sa-learn --ham /var/mail/vhosts/xoyize.xyz/yan/mail/cur/*
|
|||
|
40 02 * * * /usr/bin/sa-learn --spam /var/mail/vhosts/xoyize.xyz/yan/mail/.Spam/cur/*
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Spamassassin est très intelligent, il peut apprendre tout seul au fur et à mesure que vous recevez des mails, lorsque vous ajoutez manuellement un mail dans le dossier spam (ce qui veut dire qu’il ne l’avait pas détecté en tant que tel), <strong>sa-learn –spam</strong> permet de mettre à jour le filtrage bayésien (http://fr.wikipedia.org/wiki/Filtrage_bay%C3%A9sien_du_spam) en analysant les mails contenus dans le dossier spam, donc Spamassassin s’améliore un peu chaque jour en fonction de vous, si c’est pas cool ça</p>
|
|||
|
|
|||
|
<p>Le contraire est aussi possible avec la commande <strong>sa-learn –ham</strong>, si Spamassassin considère un mail en tant que spam alors que ce n’est pas le cas (faux positif), vous pouvez lui indiquer en remettant le mail dans le dossier principal de réception (INBOX).</p>
|
|||
|
|
|||
|
<p><strong>Correction erreur</strong> <em>spamd: failed to create readable default_prefs: /var/lib/spamassassin/.spamassassin/user_prefs</em><br />
|
|||
|
Ajout utilisateur <strong>debian-spamd</strong> à la ligne OPTIONS du fichier <strong>/etc/default/spamassassin</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>OPTIONS="--create-prefs --max-children 5 --helper-home-dir -u debian-spamd"
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Modifier les droits sur le dossier <strong>.spamassassin</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chown debian-spamd -R /var/lib/spamassassin/.spamassassin
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Redémarrer <strong>spamassassin</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl restart spamassassin
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="dovecot-sieve-filtrer-les-mails-côté-serveur">Dovecot Sieve (Filtrer les mails côté serveur)</h3>
|
|||
|
|
|||
|
<p>Vous connaissez très certainement les filtres côté clients, tout bon client mail possède un système de filtre permettant de trier automatiquement les mails en fonction de différents critères que vous pouvez définir. <strong>Sieve</strong> c’est exactement la même chose mais côté serveur, il possède un langage de script pour définir soit même l’ensemble des règles.</p>
|
|||
|
|
|||
|
<p>Pour installer sieve, exécuter la commande suivante :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install dovecot-sieve dovecot-managesieved
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Éditer le fichier principal de configuration de Dovecot et ajouter <strong>sieve</strong> dans la liste des protocoles :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/dovecot/dovecot.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>protocols = imap lmtp sieve
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ensuite dans le fichier <strong>/etc/dovecot/conf.d/20-lmtp.conf</strong>, ajouter le contenu suivant :</p>
|
|||
|
|
|||
|
<p>protocol lmtp {
|
|||
|
postmaster_address = postmaster@xoyize.xyz
|
|||
|
mail_plugins = $mail_plugins sieve
|
|||
|
}</p>
|
|||
|
|
|||
|
<p>Dans le fichier <strong>/etc/dovecot/conf.d/90-sieve.conf</strong>, modifier la configuration du plugin :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>plugin {
|
|||
|
|
|||
|
sieve = /var/mail/vhosts/%d/%n/.dovecot.sieve
|
|||
|
sieve_default = /var/mail/sieve/default.sieve
|
|||
|
sieve_dir = /var/mail/vhosts/%d/%n/sieve
|
|||
|
sieve_global_dir = /var/mail/sieve
|
|||
|
|
|||
|
}
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Puis :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir /var/mail/sieve/
|
|||
|
touch /var/mail/sieve/default.sieve && chown -R vmail:vmail /var/mail/sieve
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Par défaut, vous pouvez mettre toutes les règles dans le fichier <strong>default.sieve</strong>, elles s’appliqueront à toutes les adresses, les règles spécifiques à une adresse doivent être mises dans le fichier <strong>/var/mail/vhosts/xoyize.xyz/adresse/.dovecot.sieve</strong>. Attention si ce fichier existe, le fichier par défaut (default.sieve) ne sera pas lu pour l’utilisateur courant.</p>
|
|||
|
|
|||
|
<p>Pour ce tutoriel, on va ajouter dans <strong>/var/mail/sieve/default.sieve</strong> une règle basique mais très utile :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>require ["fileinto"];
|
|||
|
|
|||
|
if header :contains "Subject" "*****SPAM*****" {
|
|||
|
|
|||
|
fileinto "Spam";
|
|||
|
|
|||
|
}
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Je pense que c’est assez explicite pour comprendre le but de cette règle<br />
|
|||
|
Et pour finir compiler les règles avec la commande sievec :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sievec /var/mail/sieve/default.sieve
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>et on redémarre Dovecot :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl restart dovecot
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vous pouvez tester la règle en envoyant un email depuis un autre fournisseur de mail avec le contenu suivant :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Si tout fonctionne bien vous devriez recevoir le mail automatiquement dans le dossier Spam (Junk). N’hésitez pas à aller voir la documentation, pour créer d’autres règles. L’avantage de ce système c’est que les règles sont côté serveur donc elles s’appliquent à tous vos clients mail, pas besoin de les refaire lorsque vous changez de client.</p>
|
|||
|
|
|||
|
<p>Liens utiles :<br />
|
|||
|
http://wiki2.dovecot.org/Pigeonhole/Sieve<br />
|
|||
|
http://wiki2.dovecot.org/Pigeonhole/Sieve/Examples</p>
|
|||
|
|
|||
|
<p><strong>Rainloop</strong> intègre le support de Sieve directement depuis le webmail, nous allons avoir comment activer cette fonctionnalité. Connectez-vous avec votre compte administrateur à rainloop puis aller dans Domains > cliquez sur votre domaine puis sur Sieve configuration et enfin configurer comme sur l’image suivante :</p>
|
|||
|
|
|||
|
<p><img src="/images/rainloop-sieve.png" alt="Rainloop sieve" /></p>
|
|||
|
|
|||
|
<p>Cliquer sur le bouton “Test” pour tester la connexion avec SieveManager, si c’est bon, il passe au vert.
|
|||
|
Maintenant il faut se connecter à un compte utilisateur, aller dans les paramètres > Filtres. Vous pouvez dès à présent configurer vos filtres depuis cette interface.</p>
|
|||
|
|
|||
|
<p>Il existe aussi un plugin similaire pour Thunderbird ici : <a href="https://addons.mozilla.org/fr/thunderbird/addon/sieve/">https://addons.mozilla.org/fr/thunderbird/addon/sieve/</a></p>
|
|||
|
|
|||
|
<h3 id="clamav-antivirus">ClamAV (Antivirus)</h3>
|
|||
|
|
|||
|
<p><img src="/images/clamav1.png" alt="Clamav" /></p>
|
|||
|
|
|||
|
<p><em>ClamAV est un antivirus destiné aux systèmes UNIX principalement. Il est capable de détecter en temps réel des logiciels malveillants et des virus grâce à une base de détection de plus de 3 500 000 signatures. ClamAV est généralement utilisé avec Postfix pour filtrer les emails comportant des virus, malwares…etc</em></p>
|
|||
|
|
|||
|
<p>Liens utiles :<br />
|
|||
|
<a href="http://www.clamav.net/index.html">http://www.clamav.net/index.html</a><br />
|
|||
|
<a href="https://github.com/vrtadmin/clamav-faq/raw/master/manual/clamdoc.pdf">https://github.com/vrtadmin/clamav-faq/raw/master/manual/clamdoc.pdf</a><br />
|
|||
|
<a href="http://fr.wikipedia.org/wiki/ClamAV">http://fr.wikipedia.org/wiki/ClamAV</a><br />
|
|||
|
<a href="https://lelutin.ca/posts/installing_postfix_-_clamav_-_spamassassin_-_dovecot_-_postfixadmin_on_debian_squeeze/">https://lelutin.ca/posts/installing_postfix_-_clamav_-_spamassassin_-_dovecot_-_postfixadmin_on_debian_squeeze/</a></p>
|
|||
|
|
|||
|
<p>Installer le paquet clamav-milter depuis les dépôts officiels de Debian :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install clamav-milter
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>4 autres paquets seront installés en même temps :</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>clamav : C’est le paquet principal</li>
|
|||
|
<li>clamav-base : C’est le paquet de base de ClamAV, contient divers outils pour son fonctionnement</li>
|
|||
|
<li>clamav-daemon : Permet de faire tourner ClamAV en tâche de fond</li>
|
|||
|
<li>clamav-freshclam : Outil permettant de mettre à jour la base de signature des virus</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Ensuite, il faut lui dire de laisser postfix avoir accès en écriture à sa socket.(assurer que vous avez les éléments suivants dans votre fichier <strong>/etc/clamav/clamav-milter.conf</strong>)</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MilterSocketGroup postfix
|
|||
|
MilterSocketMode 660
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Postfix fonctionne dans un chroot par défaut, donc nous allons devoir reconfigurer le milter pour placer sa socket dans le répertoire chroot de postfix. Mais d’abord, nous devons créer un répertoire pour le socket UNIX et on définit clamav en tant que propriétaire :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir /var/spool/postfix/clamav
|
|||
|
chown clamav /var/spool/postfix/clamav
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Nous voulons également modifier les mesures prises lorsqu’un courriel infecté est détecté pour le rejeter immédiatement. Pour nous assurer que notre configuration reste stable grâce aux mises à jour, nous allons le faire à la manière debian:</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dpkg-reconfigure clamav-milter
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Repondez aux questions avec les réponses ci-dessous :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Faut-il gérer le fichier de configuration automatiquement ? Oui
|
|||
|
Identifiant qui exécutera clamav-milter : clamav
|
|||
|
Groupes de clamav-milter (séparés par des espaces) : laisser le champ vide
|
|||
|
Interface de communication avec Sendmail : /var/spool/postfix/clamav/clamav-milter.ctl
|
|||
|
Groupe propriétaire du fichier « socket » de clamav-milter : clamav
|
|||
|
Autorisations du fichier « socket » de clamav-milter : 666
|
|||
|
Faut-il supprimer la « socket » résiduelle après un arrêt brutal ? Oui
|
|||
|
Délai d'expiration pour les données provenant de clamd : 120
|
|||
|
Faut-il interdire à clamav-milter de créer des processus fils (« fork ») ? No
|
|||
|
Répertoire de l'environnement d'exécution sécurisé : laisser le champ vide
|
|||
|
Fichier PID : /var/run/clamav/clamav-milter.pid
|
|||
|
Chemin des répertoires temporaires : /tmp
|
|||
|
« Socket » de clamd à utiliser pour le traitement : unix:/var/spool/postfix/clamav/clamav-milter.ctl
|
|||
|
Hôtes à exclure de la vérification : laisser le champ vide
|
|||
|
Liste blanche d'adresses de courriel : laisser le champ vide
|
|||
|
Action à réaliser pour un courriel « infecté » : Rejeter
|
|||
|
Action à réaliser en cas d'erreur : Différer
|
|||
|
Motif de rejet des courriels infectés : Rejecting harmful e-mail: %v found.
|
|||
|
Faut-il ajouter des en-têtes aux courriels vérifiés ? Remplacer
|
|||
|
Fichier de journal de clamav-milter : /var/log/clamav/clamav-milter.log
|
|||
|
Faut-il désactiver le verrouillage du fichier de journal ? Non
|
|||
|
Taille maximale (en Mo) du fichier de journal : 0
|
|||
|
Faut-il ajouter des informations temporelles dans le fichier de journal pour chaque courriel ? Oui
|
|||
|
Faut-il utiliser le système de journalisation syslog ? Non
|
|||
|
Type des messages syslog : LOG_LOCAL6
|
|||
|
Faut-il activer la journalisation bavarde ? Non
|
|||
|
Niveau d'information à journaliser pour les messages infectés : Désactivé
|
|||
|
Niveau d'information à journaliser en l'absence d'alerte : Désactivé
|
|||
|
Taille maximale (en Mo) des fichiers à vérifier : 25
|
|||
|
Voulez-vous que clamav-milter gère plusieurs destinataires ? Non
|
|||
|
Souhaitez-vous activer la rotation des journaux ? Oui
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>La reconfiguration ci-dessus aurait dû redémarrer automatiquement clamav-milter. Vérifier que la prise est en place. Normalement, le démon clam doit être activé dans la procédure de démarrage, mais il est possible que vous ayez besoin de le démarrer manuellement juste après l’installation.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> service clamav-daemon start
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ensuite, nous allons configurer Postfix pour qu’il utilise le milter pour inspecter les e-mails entrants. Rappelez-vous que le processus smtp est exécuté dans un chroot, donc le chemin doit avoir sa racine en haut du répertoire chroot</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>postconf -e 'smtpd_milters = unix:/clamav/clamav-milter.ctl'
|
|||
|
postfix reload
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><strong>Test du filtre en envoyant un virus</strong><br />
|
|||
|
Pour tester cela, nous devrons envoyer un fichier malveillant au serveur SMTP dans l’espoir qu’il le bloque. Pour cela, vous pouvez installer le paquet <strong>clamav-testfiles</strong> et <strong>swaks</strong> sur votre ordinateur; il installera des fichiers d’exemple de virus sous <strong>/usr/share/clamav-testfiles</strong>.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install clamav-testfiles swaks
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Envoyons-en une comme pièce jointe à un courriel:<br />
|
|||
|
<code class="language-plaintext highlighter-rouge">swaks --to=yannick@xoyize.xyz --server=vps26381 --attach - --suppress-data < /usr/share/clamav-testfiles/clam.exe</code></p>
|
|||
|
|
|||
|
<p>Par défaut, freshclam récupère les dernières mises à jour depuis le serveur principal database.clamav.net, on peut spécifier d’autres serveurs, plus proches, par exemple : db.fr.clamav.net</p>
|
|||
|
|
|||
|
<p>Dans le fichier <strong>/etc/clamav/freshclam.conf</strong>, ajouter la ligne suivante au-dessus des autres serveurs “DatabaseMirror” :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DatabaseMirror db.fr.clamav.net
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Sous <strong>Debian 7</strong>, ajoutez une nouvelle tâche CRON pour mettre à jour la base de signature automatiquement toutes les heures.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>crontab -e
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>15 * * * * /usr/bin/freshclam –quiet</p>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p><strong>La mise à jour est effectuée automatiquement sous Debian 8 Jessie, la tâche CRON n’est plus nécessaire.</strong></p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p>On redémarre le daemon pour prendre en compte les modifications :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>service clamav-daemon restart
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Il ne reste plus qu’à dire à Postfix où se trouve le socket UNIX de clamav dans le fichier <strong>/etc/postfix/main.cf</strong> :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>smtpd_milters = unix:/var/spool/postfix/opendkim/opendkim.sock, unix:/var/spool/postfix/opendmarc/opendmarc.sock, unix:/var/spool/postfix/clamav/clamav-milter.ctl
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Recharger la configuration de Postfix :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl reload postfix
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Une fois que tous les paquets sont installés, il faut mettre à jour la base de signature avec freshclam :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl stop clamav-freshclam
|
|||
|
freshclam
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ClamAV update process started at Thu Feb 15 18:25:17 2018
|
|||
|
Downloading main.cvd [100%]
|
|||
|
main.cvd updated (version: 58, sigs: 4566249, f-level: 60, builder: sigmgr)
|
|||
|
Downloading daily.cvd [100%]
|
|||
|
daily.cvd updated (version: 24010, sigs: 1769510, f-level: 63, builder: neo)
|
|||
|
Downloading bytecode.cvd [100%]
|
|||
|
bytecode.cvd updated (version: 315, sigs: 75, f-level: 63, builder: raynman)
|
|||
|
...
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl start clamav-freshclam
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ensuite on démarre le daemon :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl start clamav-daemon
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Il faut donner les droits <strong>clamv</strong> aux dossiers</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chown -R clamav:clamav /var/lib/clamav
|
|||
|
chown -R clamav:clamav /var/log/clamav
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="fail2ban">Fail2ban</h3>
|
|||
|
|
|||
|
<p><img src="/images/fail2ban.png" alt="Fail2ban" /></p>
|
|||
|
|
|||
|
<p><em>Fail2ban lit des fichiers de log et bannit les adresses IP qui ont obtenu un trop grand nombre d’échecs lors de l’authentification. Il met à jour les règles du pare-feu pour rejeter cette adresse IP. Ces règles peuvent êtres défines par l’utilisateur.</em></p>
|
|||
|
|
|||
|
<p>Les commandes sont exécutées en mode su sudo</p>
|
|||
|
|
|||
|
<p>Installation</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install fail2ban
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><strong>fail2ban.conf</strong></p>
|
|||
|
|
|||
|
<p>Rien à faire dans ce fichier, vous pouvez laisser les options par défaut:</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/fail2ban/fail2ban.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Definition]
|
|||
|
|
|||
|
# Option: loglevel
|
|||
|
# Notes.: Set the log level output.
|
|||
|
# CRITICAL
|
|||
|
# ERROR
|
|||
|
# WARNING
|
|||
|
# NOTICE
|
|||
|
# INFO
|
|||
|
# DEBUG
|
|||
|
# Values: [ LEVEL ] Default: ERROR
|
|||
|
#
|
|||
|
loglevel = INFO
|
|||
|
|
|||
|
# Option: logtarget
|
|||
|
# Notes.: Set the log target. This could be a file, SYSLOG, STDERR or STDOUT.
|
|||
|
# Only one log target can be specified.
|
|||
|
# If you change logtarget from the default value and you are
|
|||
|
# using logrotate -- also adjust or disable rotation in the
|
|||
|
# corresponding configuration file
|
|||
|
# (e.g. /etc/logrotate.d/fail2ban on Debian systems)
|
|||
|
# Values: [ STDOUT | STDERR | SYSLOG | FILE ] Default: STDERR
|
|||
|
#
|
|||
|
logtarget = /var/log/fail2ban.log
|
|||
|
|
|||
|
# Option: socket
|
|||
|
# Notes.: Set the socket file. This is used to communicate with the daemon. Do
|
|||
|
# not remove this file when Fail2ban runs. It will not be possible to
|
|||
|
# communicate with the server afterwards.
|
|||
|
# Values: [ FILE ] Default: /var/run/fail2ban/fail2ban.sock
|
|||
|
#
|
|||
|
socket = /var/run/fail2ban/fail2ban.sock
|
|||
|
|
|||
|
# Option: pidfile
|
|||
|
# Notes.: Set the PID file. This is used to store the process ID of the
|
|||
|
# fail2ban server.
|
|||
|
# Values: [ FILE ] Default: /var/run/fail2ban/fail2ban.pid
|
|||
|
#
|
|||
|
pidfile = /var/run/fail2ban/fail2ban.pid
|
|||
|
|
|||
|
# Options: dbfile
|
|||
|
# Notes.: Set the file for the fail2ban persistent data to be stored.
|
|||
|
# A value of ":memory:" means database is only stored in memory
|
|||
|
# and data is lost when fail2ban is stopped.
|
|||
|
# A value of "None" disables the database.
|
|||
|
# Values: [ None :memory: FILE ] Default: /var/lib/fail2ban/fail2ban.sqlite3
|
|||
|
dbfile = None
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><strong>jail.conf</strong></p>
|
|||
|
|
|||
|
<p>Copier la configuration par défaut afin qu’elle ne soit pas supprimée en cas de mise à jour.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ce fichier est beaucoup plus intéressant, il contient tous les services à monitorer, et vous allez le découvrir, fail2ban ne se limite pas à SSH…</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/fail2ban/jail.local
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>Tous les commentaires ont été supprimés pour allèger le fichier.<br />
|
|||
|
Les commentaires sont visibles dans le fichier original <strong>jail.conf</strong></p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[INCLUDES]
|
|||
|
|
|||
|
before = paths-debian.conf
|
|||
|
|
|||
|
[DEFAULT]
|
|||
|
|
|||
|
ignoreip = 127.0.0.1/8
|
|||
|
bantime = 600
|
|||
|
findtime = 600
|
|||
|
maxretry = 3
|
|||
|
backend = auto
|
|||
|
usedns = warn
|
|||
|
logencoding = auto
|
|||
|
filter = %(__name__)s
|
|||
|
|
|||
|
destemail = root@localhost
|
|||
|
sender = root@localhost
|
|||
|
mta = sendmail
|
|||
|
protocol = tcp
|
|||
|
chain = INPUT
|
|||
|
port = 0:65535
|
|||
|
fail2ban_agent = Fail2Ban/%(fail2ban_version)s
|
|||
|
|
|||
|
banaction = iptables-multiport
|
|||
|
banaction_allports = iptables-allports
|
|||
|
protocol="%(protocol)s", chain="%(chain)s"]
|
|||
|
action_mw = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
|||
|
%(mta)s-whois[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"]
|
|||
|
|
|||
|
action_mwl = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
|||
|
%(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]
|
|||
|
|
|||
|
action_xarf = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
|||
|
xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath=%(logpath)s, port="%(port)s"]
|
|||
|
|
|||
|
action_cf_mwl = cloudflare[cfuser="%(cfemail)s", cftoken="%(cfapikey)s"]
|
|||
|
%(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]
|
|||
|
|
|||
|
action_blocklist_de = blocklist_de[email="%(sender)s", service=%(filter)s, apikey="%(blocklist_de_apikey)s", agent="%(fail2ban_agent)s"]
|
|||
|
|
|||
|
action_badips = badips.py[category="%(__name__)s", banaction="%(banaction)s", agent="%(fail2ban_agent)s"]
|
|||
|
action_badips_report = badips[category="%(__name__)s", agent="%(fail2ban_agent)s"]
|
|||
|
|
|||
|
action = %(action_)s
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour envoyer un mail contenant le whois, placez la variable sur :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>action = %(action_mw)s
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour envoyer un mail avec le whois ET les logs, placez la variable sur :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>action = %(action_mwl)s
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour activer la surveillance d’un service, il suffit de placer la variable “enabled” à “true”</p>
|
|||
|
|
|||
|
<p>Par défaut la protection du service SSH est activée, pas les autres.</p>
|
|||
|
|
|||
|
<p>Si votre ssh n’écoute pas sur le port 22, pensez à le changer… (port = N° de port).</p>
|
|||
|
|
|||
|
<p>Rappelez vous également que le loglevel de SSHD (<strong>/etc/ssh/sshd_config</strong>) doit absolument être positionné sur DEBUG (<strong>LogLevel DEBUG</strong>) sans quoi, Fail2ban ne bloquera rien concernant SSH.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#
|
|||
|
# JAILS
|
|||
|
#
|
|||
|
|
|||
|
# SSH
|
|||
|
[sshd]
|
|||
|
|
|||
|
enabled = true
|
|||
|
port = 55026
|
|||
|
logpath = %(sshd_log)s
|
|||
|
backend = %(sshd_backend)s
|
|||
|
|
|||
|
# Mail servers
|
|||
|
[postfix]
|
|||
|
|
|||
|
enabled = true
|
|||
|
port = smtp,ssmtp,submission
|
|||
|
filter = postfix
|
|||
|
logpath = /var/log/mail.log
|
|||
|
|
|||
|
[sasl]
|
|||
|
|
|||
|
enabled = true
|
|||
|
port = smtp,ssmtp,submission,imap2,imap3,imaps,pop3,pop3s
|
|||
|
filter = postfix-sasl
|
|||
|
# You might consider monitoring /var/log/mail.warn instead if you are
|
|||
|
# running postfix since it would provide the same log lines at the
|
|||
|
# "warn" level but overall at the smaller filesize.
|
|||
|
logpath = /var/log/mail.log
|
|||
|
|
|||
|
[dovecot]
|
|||
|
|
|||
|
enabled = true
|
|||
|
port = smtp,ssmtp,submission,imap2,imap3,imaps,pop3,pop3s
|
|||
|
filter = dovecot
|
|||
|
logpath = /var/log/mail.log
|
|||
|
|
|||
|
# nginx
|
|||
|
[nginx-http-auth]
|
|||
|
|
|||
|
enabled = true
|
|||
|
port = http,https
|
|||
|
logpath = %(nginx_error_log)s
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Redémarrer fail2ban pour implémenter les règles:</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sytemctl restart fail2ban
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Afficher les règles de pare-feu actuelles</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo iptables -S
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-P INPUT DROP
|
|||
|
-P FORWARD DROP
|
|||
|
-P OUTPUT ACCEPT
|
|||
|
-N f2b-dovecot
|
|||
|
-N f2b-nginx-http-auth
|
|||
|
-N f2b-postfix
|
|||
|
-N f2b-sasl
|
|||
|
-N f2b-sshd
|
|||
|
-A INPUT -p tcp -m multiport --dports 25,465,587 -j f2b-postfix
|
|||
|
-A INPUT -p tcp -m multiport --dports 80,443 -j f2b-nginx-http-auth
|
|||
|
-A INPUT -p tcp -m multiport --dports 55026 -j f2b-sshd
|
|||
|
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
|
|||
|
-A INPUT -i lo -j ACCEPT
|
|||
|
-A INPUT -p icmp -j ACCEPT
|
|||
|
-A INPUT -p tcp -m tcp --dport 55026 -j ACCEPT
|
|||
|
-A INPUT -p tcp -m tcp --dport 53 -j ACCEPT
|
|||
|
-A INPUT -p udp -m udp --dport 53 -j ACCEPT
|
|||
|
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
|
|||
|
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
|
|||
|
-A INPUT -p tcp -m tcp --dport 993 -j ACCEPT
|
|||
|
-A INPUT -p tcp -m tcp --dport 587 -j ACCEPT
|
|||
|
-A INPUT -p tcp -m tcp --dport 25 -j ACCEPT
|
|||
|
-A OUTPUT -o lo -j ACCEPT
|
|||
|
-A OUTPUT -p tcp -m tcp --dport 55026 -j ACCEPT
|
|||
|
-A OUTPUT -p tcp -m tcp --dport 53 -j ACCEPT
|
|||
|
-A OUTPUT -p udp -m udp --dport 53 -j ACCEPT
|
|||
|
-A OUTPUT -p tcp -m tcp --dport 80 -j ACCEPT
|
|||
|
-A OUTPUT -p tcp -m tcp --dport 443 -j ACCEPT
|
|||
|
-A OUTPUT -p tcp -m tcp --dport 993 -j ACCEPT
|
|||
|
-A OUTPUT -p tcp -m tcp --dport 587 -j ACCEPT
|
|||
|
-A OUTPUT -p tcp -m tcp --dport 25 -j ACCEPT
|
|||
|
-A f2b-dovecot -j RETURN
|
|||
|
-A f2b-nginx-http-auth -j RETURN
|
|||
|
-A f2b-postfix -j RETURN
|
|||
|
-A f2b-sasl -j RETURN
|
|||
|
-A f2b-sshd -j RETURN
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h2 id="clients-de-messagerie">Clients de messagerie</h2>
|
|||
|
|
|||
|
<h3 id="rainloop-webmail">Rainloop (webmail)</h3>
|
|||
|
|
|||
|
<p><img src="/images/rainloop1.png" alt="webmail rainloop" /></p>
|
|||
|
|
|||
|
<p>Rainloop est un webmail opensource développé en PHP qui se veut complet et simple d’utilisation. Il gère très bien les protocoles IMAP/SMTP et dispose d’une interface moderne (HTML5/CSS3) très érgonomique, c’est plutôt agréable. Du côté des fonctionnalités, on retrouve toutes celles d’un client mail classique, avec en plus un système de plugins.</p>
|
|||
|
|
|||
|
<p>Rainloop est très simple à mettre en place. Téléchargez le zip depuis le site officiel et décompressez-le dans le dossier <strong>/var/www/rainloop</strong> :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget https://www.rainloop.net/repository/webmail/rainloop-community-latest.zip
|
|||
|
mkdir /var/www/rainloop
|
|||
|
unzip rainloop-community-latest.zip -d /var/www/rainloop
|
|||
|
rm -rf rainloop-community-latest.zip
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Modifiez les permissions pour que le serveur web ait accès au répertoire <strong>/var/www/rainloop</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd /var/www/rainloop
|
|||
|
find . -type d -exec chmod 755 {} \;
|
|||
|
find . -type f -exec chmod 644 {} \;
|
|||
|
chown -R www-data:www-data .
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajouter un nouveau virtual host Nginx (à adapter selon votre configuration) :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir -p /etc/nginx/conf.d/webmail.xoyize.xyz.d
|
|||
|
nano /etc/nginx/conf.d/webmail.xoyize.xyz.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server {
|
|||
|
listen 443 ssl http2;
|
|||
|
listen [::]:443 ssl http2;
|
|||
|
server_name webmail.xoyize.xyz;
|
|||
|
|
|||
|
#### Locations
|
|||
|
# On cache les fichiers statiques
|
|||
|
location ~* \.(html|css|js|png|jpg|jpeg|gif|ico|svg|eot|woff|ttf)$ { expires max; }
|
|||
|
# On interdit les dotfiles
|
|||
|
location ~ /\. { deny all; }
|
|||
|
|
|||
|
include ssl_params;
|
|||
|
include header_params;
|
|||
|
include dh_param;
|
|||
|
|
|||
|
root /var/www/rainloop ;
|
|||
|
index index.php index.html index.htm;
|
|||
|
location ~ \.php$ {
|
|||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
|||
|
fastcgi_pass unix:/run/php/php7.0-fpm.sock; # PHP7.0
|
|||
|
fastcgi_index index.php;
|
|||
|
include fastcgi_params;
|
|||
|
fastcgi_param SCRIPT_FILENAME $request_filename;
|
|||
|
}
|
|||
|
|
|||
|
include conf.d/webmail.xoyize.xyz.d/*.conf;
|
|||
|
|
|||
|
access_log /var/log/nginx/webmail.xoyize.xyz-access.log;
|
|||
|
error_log /var/log/nginx/webmail.xoyize.xyz-error.log;
|
|||
|
}
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Relancer le service nginx</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl restart nginx
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Connexion à l’interface d’administration via le lien <a href="https://webmail.xoyize.xyz/?admin">https://webmail.xoyize.xyz/?admin</a><br />
|
|||
|
Par défaut les identifiants sont : admin et 12345</p>
|
|||
|
|
|||
|
<p>Modifier le mot de passe “admin”<br />
|
|||
|
Une fois connecté à l’interface d’administration, vous devez ajouter un nouveau domaine. Dans le menu de gauche cliquez sur <strong>Domaines</strong> puis sur + <strong>Ajouter un domaine</strong>. Une nouvelle fenêtre s’ouvre et vous demande de configurer IMAP et SMTP pour ce domaine.<br />
|
|||
|
IMAP xoyize.xyz 993 SSL/TLS<br />
|
|||
|
SMTP xoyize.xyz 587 STARTTLS</p>
|
|||
|
|
|||
|
<p><img src="/images/rainloop-domaine1.png" alt="rainloop domaine" /></p>
|
|||
|
|
|||
|
<p>Une fois les champs remplis, cliquez sur le bouton “Test Connection” pour voir si vous n’avez pas fait d’erreur de saisie. N’hésitez pas à lancer en même temps la commande tail si vous avez des problèmes de connexion :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tail -f /var/log/mail.log
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajoutez tous les utilisateurs autorisés à se connecter dans le White List (bouton en bas), par exemple pour admin@xoyize.xyz et yan@xoyize.xyz vous devez mettre (les utilisateurs sont séparés par des espaces) :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>admin yan
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Voir les autres sections de l’espace d’administration pour une personnalisation.<br />
|
|||
|
Activation des logs</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano nano /var/www/rainloop/data/_data_/_default_/configs/application.ini
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[logs]
|
|||
|
; Enable logging
|
|||
|
enable = On
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="thunderbird-linux-k9mail-android">Thunderbird (linux), K9mail (Android)</h3>
|
|||
|
|
|||
|
<p>La configuration d’un client mail reste relativement simple, il ne faut juste pas se tromper de port :</p>
|
|||
|
|
|||
|
<p><img src="/images/yan-xoyize-xyz1.png" alt="thunderbird" title="exemple configuration thunderbird" /></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>IMAP imap.xoyize.xyz ou xoyize.xyz 993 SSL/TLS Normal Password
|
|||
|
SMTP smtp.xoyize.xyz ou xoyize.xyz 587 STARTTLS Normal Password
|
|||
|
Utilisateur yan@xoyize.xyz
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>Les adresses imap.xoyize.xyz et smtp.xoyize.xyz sont arbitraires, vous pouvez très bien mettre à la place le FQDN de votre serveur (exemple: xoyize.xyz), ça marchera très bien aussi. Le plus important ce sont les ports et les algorithmes de chiffrement/d’authentification (SSL/TLS - STARTTLS).</p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<h2 id="tests">Tests</h2>
|
|||
|
|
|||
|
<p>Tester avec la commande openssl</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>openssl s_client -connect xoyize.xyz:587 -starttls smtp -tlsextdebug # Postfix
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Il faut vérifier la chaîne :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>---
|
|||
|
Certificate chain
|
|||
|
0 s:/CN=xoyize.xyz
|
|||
|
i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
|
|||
|
1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
|
|||
|
i:/O=Digital Signature Trust Co./CN=DST Root CA X3
|
|||
|
---
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Puis taper <code class="language-plaintext highlighter-rouge">QUIT</code></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>openssl s_client -connect xoyize.xyz:993 -tlsextdebug # Dovecot
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Dans le cas de IMAP, le serveur devrait répondre :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>* OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE AUTH=PLAIN AUTH=LOGIN] Dovecot ready.
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Puis taper <code class="language-plaintext highlighter-rouge">QUIT</code></p>
|
|||
|
|
|||
|
<h2 id="nextcloud">Nextcloud</h2>
|
|||
|
|
|||
|
<h3 id="installation">Installation</h3>
|
|||
|
|
|||
|
<p>Prérequis</p>
|
|||
|
|
|||
|
<p>Base Mysql/Mariadb</p>
|
|||
|
|
|||
|
<p>Installer la dernière version nextcloud</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd /var/www
|
|||
|
wget https://download.nextcloud.com/server/releases/nextcloud-13.0.0.zip
|
|||
|
unzip nextcloud-13.0.0.zip
|
|||
|
mkdir /var/www/nextcloud/data
|
|||
|
rm nextcloud-13.0.0.zip
|
|||
|
useradd nextcloud --comment "limited nextcloud user" --no-create-home
|
|||
|
chown -R nextcloud:www-data /var/www/nextcloud
|
|||
|
chmod -R o-rwx /var/www/nextcloud
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Création du pool dédié à Nextcloud</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/php/7.0/fpm/pool.d/nextcloud.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[nextcloud]
|
|||
|
|
|||
|
listen = /run/php/php7.0-fpm-nextcloud.sock
|
|||
|
|
|||
|
; Set permissions for unix socket, if one is used.
|
|||
|
listen.owner = nextcloud
|
|||
|
listen.group = www-data
|
|||
|
listen.mode = 0660
|
|||
|
|
|||
|
; Unix user/group of processes.
|
|||
|
user = nextcloud
|
|||
|
group = www-data
|
|||
|
|
|||
|
pm = dynamic
|
|||
|
pm.max_children = 6
|
|||
|
pm.start_servers = 3
|
|||
|
pm.min_spare_servers = 3
|
|||
|
pm.max_spare_servers = 5
|
|||
|
pm.max_requests = 500
|
|||
|
pm.status_path = /fpm-status
|
|||
|
ping.path = /ping
|
|||
|
request_terminate_timeout = 1d
|
|||
|
request_slowlog_timeout = 5s
|
|||
|
slowlog = /var/log/nginx/nextcloud.slow.log
|
|||
|
rlimit_files = 4096
|
|||
|
rlimit_core = 0
|
|||
|
chdir = /var/www/nextcloud/
|
|||
|
catch_workers_output = yes
|
|||
|
clear_env = no
|
|||
|
|
|||
|
php_value[upload_max_filesize] = 10G
|
|||
|
php_value[post_max_size] = 10G
|
|||
|
php_value[default_charset] = UTF-8
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Redémarrez le service php-fpm afin d’activer le nouveau pool nextcloud :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl restart php7.0-fpm.service
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>La configuration vhost</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/nginx/conf.d/xoyize.xyz.d/nextcloud.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>location ^~ /nextcloud {
|
|||
|
alias /var/www/nextcloud/;
|
|||
|
|
|||
|
if ($scheme = http) {
|
|||
|
rewrite ^ https://$server_name$request_uri? permanent;
|
|||
|
}
|
|||
|
|
|||
|
# Add headers to serve security related headers
|
|||
|
add_header X-Content-Type-Options nosniff;
|
|||
|
add_header X-XSS-Protection "1; mode=block";
|
|||
|
add_header X-Robots-Tag none;
|
|||
|
add_header X-Download-Options noopen;
|
|||
|
add_header X-Permitted-Cross-Domain-Policies none;
|
|||
|
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains;';
|
|||
|
|
|||
|
# Set max upload size
|
|||
|
client_max_body_size 10G;
|
|||
|
fastcgi_buffers 64 4K;
|
|||
|
|
|||
|
# Disable gzip to avoid the removal of the ETag header
|
|||
|
gzip off;
|
|||
|
|
|||
|
# Errors pages
|
|||
|
error_page 403 /nextcloud/core/templates/403.php;
|
|||
|
error_page 404 /nextcloud/core/templates/404.php;
|
|||
|
|
|||
|
# The following 2 rules are only needed for the user_webfinger app.
|
|||
|
# Uncomment it if you're planning to use this app.
|
|||
|
#rewrite ^/.well-known/host-meta /nextcloud/public.php?service=host-meta last;
|
|||
|
#rewrite ^/.well-known/host-meta.json /nextcloud/public.php?service=host-meta-json last;
|
|||
|
|
|||
|
location /nextcloud {
|
|||
|
rewrite ^ /nextcloud/index.php$uri;
|
|||
|
}
|
|||
|
|
|||
|
location = /nextcloud/robots.txt {
|
|||
|
allow all;
|
|||
|
log_not_found off;
|
|||
|
access_log off;
|
|||
|
}
|
|||
|
|
|||
|
location ~ ^/nextcloud/(?:build|tests|config|lib|3rdparty|templates|data)/ {
|
|||
|
deny all;
|
|||
|
}
|
|||
|
location ~ ^/nextcloud/(?:\.|autotest|occ|issue|indie|db_|console) {
|
|||
|
deny all;
|
|||
|
}
|
|||
|
|
|||
|
location ~ ^/nextcloud/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+|core/templates/40[34])\.php(?:$|/) {
|
|||
|
include fastcgi_params;
|
|||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
|||
|
fastcgi_param SCRIPT_FILENAME $request_filename;
|
|||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
|||
|
fastcgi_param HTTPS on;
|
|||
|
fastcgi_param modHeadersAvailable true;
|
|||
|
fastcgi_param REMOTE_USER $remote_user;
|
|||
|
fastcgi_pass unix:/run/php/php7.0-fpm-nextcloud.sock;
|
|||
|
fastcgi_intercept_errors on;
|
|||
|
}
|
|||
|
|
|||
|
location ~ ^/nextcloud/(?:updater|ocs-provider)(?:$|/) {
|
|||
|
try_files $uri/ =404;
|
|||
|
index index.php;
|
|||
|
}
|
|||
|
|
|||
|
# Adding the cache control header for js and css files
|
|||
|
location ~* \.(?:css|js)$ {
|
|||
|
add_header Cache-Control "public, max-age=7200";
|
|||
|
# Add headers to serve security related headers
|
|||
|
add_header Strict-Transport-Security "max-age=15768000;";
|
|||
|
add_header X-Content-Type-Options nosniff;
|
|||
|
add_header X-Frame-Options "SAMEORIGIN";
|
|||
|
add_header X-XSS-Protection "1; mode=block";
|
|||
|
add_header X-Robots-Tag none;
|
|||
|
add_header X-Download-Options noopen;
|
|||
|
add_header X-Permitted-Cross-Domain-Policies none;
|
|||
|
# Optional: Don't log access to assets
|
|||
|
access_log off;
|
|||
|
}
|
|||
|
|
|||
|
location ~* \.(?:svg|gif|png|html|ttf|woff|ico|jpg|jpeg)$ {
|
|||
|
# Optional: Don't log access to other assets
|
|||
|
access_log off;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vérification</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nginx -t
|
|||
|
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
|
|||
|
nginx: configuration file /etc/nginx/nginx.conf test is successful
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Relancer php-fpm et nginx</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl restart php7.0-fpm nginx
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Accès https://xoyize.xyz/nextcloud</p>
|
|||
|
|
|||
|
<p>Créer un compte administrateur admin + mot de passe<br />
|
|||
|
Répertoire des données /var/www/nextcloud/data<br />
|
|||
|
Base MariaDb (MySql) nextcloud , utilisateur nextcloud + mot de passe accès</p>
|
|||
|
|
|||
|
<h3 id="les-caches">Les caches</h3>
|
|||
|
|
|||
|
<p><a href="https://static.ouestline.net/linux/2017/09/13/Nextcloud-Debian-Stretch.html#les-caches">Nextcloud Debian Stretch: les caches</a></p>
|
|||
|
|
|||
|
<p>Après installation du cache ,se connecter en <strong>admin</strong> , Paramètres -> Paramètres de base</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Avertissements de sécurité & configuration
|
|||
|
|
|||
|
Il est important pour la sécurité et la performance de votre instance que tout soit configuré correctement. Pour vous aider dans cette tâche, nous faisons des vérifications automatiques. Veuillez consulter la section Trucs et Astuces et la documentation pour plus d'informations.
|
|||
|
|
|||
|
x Tous les tests ont réussi.
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="activer-la-double-authentification-admin">Activer la double authentification (admin)</h3>
|
|||
|
|
|||
|
<p>Administration , Outils “Two Factor TOTP Provider” : Activer</p>
|
|||
|
|
|||
|
<p><img src="/images/2fa1.png" alt="2fa" /></p>
|
|||
|
|
|||
|
<p>Complément<br />
|
|||
|
Administartion ,Paramètres Sécurité<br />
|
|||
|
Générer des codes de récupération</p>
|
|||
|
|
|||
|
<p><img src="/images/2fa2.png" alt="2fa" /></p>
|
|||
|
|
|||
|
<p>Deuxième facteur d’authentification du mot de passe temporaire à usage unique
|
|||
|
x Activer les mots de passe à usage unique (TOTP)
|
|||
|
Votre nouveau secret TOTP est : kfkfiiehhdyyhdkz
|
|||
|
Scannez ce QR code avec votre application TOTP (android)</p>
|
|||
|
|
|||
|
<p><img src="/images/2fa3.png" alt="2fa" /></p>
|
|||
|
|
|||
|
<p>Enter un code pour confirmer le passage en double authentification</p>
|
|||
|
|
|||
|
<h3 id="utilisateur-calendar-contacts-et-mail">Utilisateur, Calendar, Contacts et Mail</h3>
|
|||
|
|
|||
|
<h4 id="connexion-admin">Connexion admin</h4>
|
|||
|
|
|||
|
<p>Créer un groupe <strong>users</strong> et ajouter un utilisateur <strong>yan</strong> à ce groupe</p>
|
|||
|
|
|||
|
<p>Administration, +Applications, Pack d’applications<br />
|
|||
|
Calendar : Activer<br />
|
|||
|
Contacts : Activer<br />
|
|||
|
External storage support :Activer</p>
|
|||
|
|
|||
|
<p>Administration, +Applications, Bureautique &Texte “Mail” : Activer</p>
|
|||
|
|
|||
|
<p><img src="/images/nextcloud-icones.png" alt="icones" /></p>
|
|||
|
|
|||
|
<p>Stockage externe</p>
|
|||
|
|
|||
|
<p><img src="/images/nextcloud-externe.png" alt="externe" /></p>
|
|||
|
|
|||
|
<h4 id="connexion-utilisateur-yan">Connexion utilisateur yan</h4>
|
|||
|
|
|||
|
<p><strong>Messagerie</strong> : Cliquer sur l’icône <img src="/images/icone-mail.png" alt="icone mail" /> (en haut à gauche de l’écran) et compléter</p>
|
|||
|
|
|||
|
<p><img src="/images/nextcloud-mail1.png" alt="Mail" /></p>
|
|||
|
|
|||
|
<p><strong>Calendrier</strong> : Cliquer sur l’icône <img src="/images/icone-calendar.png" alt="icone mail" /> et renommer l’agenda <strong>yan</strong></p>
|
|||
|
|
|||
|
<p><strong>Contacts</strong> : Cliquer sur l’icône <img src="/images/icone-contacts.png" alt="icone mail" /> et renommer le carnet d’adresses <strong>yan</strong></p>
|
|||
|
|
|||
|
<p>Activer l’authentification en deux étapes, avec les codes de récupération, pour l’utilisateur <strong>yan</strong> , Personnel -> Sécurité<br />
|
|||
|
Pour utiliser la synchronisation client , il faut aller dans Paramètres -> Sécurité et cliquer syr <strong>Créer un nouveau mot de passe d’application</strong><br />
|
|||
|
Ce sera le mot de passe à utiliser pour la synchronisation de fichiers/dossiers (Impératif quand on active la double authentification)</p>
|
|||
|
|
|||
|
<p><img src="/images/security-nextcloud.png" alt="mp appli" /></p>
|
|||
|
|
|||
|
<h3 id="nextcloud-ncxoyizexyz">Nextcloud nc.xoyize.xyz</h3>
|
|||
|
|
|||
|
<p>Pour afficher la page de connexion Nextcloud sur <a href="https://nc.xoyize.xyz">https://nc.xoyize.xyz</a><br />
|
|||
|
Créer le fichier <strong>/etc/nginx/conf.d/nc.xoyize.xyz.conf</strong> avec le contenu suivant</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>upstream php-handler {
|
|||
|
#server 127.0.0.1:9000;
|
|||
|
server unix:/run/php/php7.0-fpm-nextcloud.sock; # PHP7.0
|
|||
|
}
|
|||
|
|
|||
|
server {
|
|||
|
listen 80;
|
|||
|
listen [::]:80;
|
|||
|
server_name nc.xoyize.xyz;
|
|||
|
# enforce https
|
|||
|
return 301 https://$server_name$request_uri;
|
|||
|
}
|
|||
|
|
|||
|
server {
|
|||
|
listen 443 ssl http2;
|
|||
|
listen [::]:443 ssl http2;
|
|||
|
server_name nc.xoyize.xyz;
|
|||
|
|
|||
|
include ssl_params;
|
|||
|
include dh_param;
|
|||
|
|
|||
|
# Add headers to serve security related headers
|
|||
|
# Before enabling Strict-Transport-Security headers please read into this
|
|||
|
# topic first.
|
|||
|
# add_header Strict-Transport-Security "max-age=15768000;
|
|||
|
# includeSubDomains; preload;";
|
|||
|
#
|
|||
|
# WARNING: Only add the preload option once you read about
|
|||
|
# the consequences in https://hstspreload.org/. This option
|
|||
|
# will add the domain to a hardcoded list that is shipped
|
|||
|
# in all major browsers and getting removed from this list
|
|||
|
# could take several months.
|
|||
|
add_header X-Content-Type-Options nosniff;
|
|||
|
add_header X-XSS-Protection "1; mode=block";
|
|||
|
add_header X-Robots-Tag none;
|
|||
|
add_header X-Download-Options noopen;
|
|||
|
add_header X-Permitted-Cross-Domain-Policies none;
|
|||
|
|
|||
|
# Path to the root of your installation
|
|||
|
root /var/www/nextcloud/;
|
|||
|
|
|||
|
location = /robots.txt {
|
|||
|
allow all;
|
|||
|
log_not_found off;
|
|||
|
access_log off;
|
|||
|
}
|
|||
|
|
|||
|
# The following 2 rules are only needed for the user_webfinger app.
|
|||
|
# Uncomment it if you're planning to use this app.
|
|||
|
#rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
|
|||
|
#rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json
|
|||
|
# last;
|
|||
|
|
|||
|
location = /.well-known/carddav {
|
|||
|
return 301 $scheme://$host/remote.php/dav;
|
|||
|
}
|
|||
|
location = /.well-known/caldav {
|
|||
|
return 301 $scheme://$host/remote.php/dav;
|
|||
|
}
|
|||
|
|
|||
|
# set max upload size
|
|||
|
client_max_body_size 512M;
|
|||
|
fastcgi_buffers 64 4K;
|
|||
|
|
|||
|
# Enable gzip but do not remove ETag headers
|
|||
|
gzip on;
|
|||
|
gzip_vary on;
|
|||
|
gzip_comp_level 4;
|
|||
|
gzip_min_length 256;
|
|||
|
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
|
|||
|
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
|
|||
|
|
|||
|
# Uncomment if your server is build with the ngx_pagespeed module
|
|||
|
# This module is currently not supported.
|
|||
|
#pagespeed off;
|
|||
|
|
|||
|
location / {
|
|||
|
rewrite ^ /index.php$uri;
|
|||
|
}
|
|||
|
|
|||
|
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ {
|
|||
|
deny all;
|
|||
|
}
|
|||
|
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
|
|||
|
deny all;
|
|||
|
}
|
|||
|
|
|||
|
location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\.php(?:$|/) {
|
|||
|
fastcgi_split_path_info ^(.+\.php)(/.*)$;
|
|||
|
include fastcgi_params;
|
|||
|
#fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
|||
|
fastcgi_param SCRIPT_FILENAME $request_filename;
|
|||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
|||
|
fastcgi_param HTTPS on;
|
|||
|
#Avoid sending the security headers twice
|
|||
|
fastcgi_param modHeadersAvailable true;
|
|||
|
fastcgi_param REMOTE_USER $remote_user;
|
|||
|
fastcgi_param front_controller_active true;
|
|||
|
fastcgi_pass php-handler;
|
|||
|
fastcgi_intercept_errors on;
|
|||
|
fastcgi_request_buffering off;
|
|||
|
}
|
|||
|
|
|||
|
location ~ ^/(?:updater|ocs-provider)(?:$|/) {
|
|||
|
try_files $uri/ =404;
|
|||
|
index index.php;
|
|||
|
}
|
|||
|
|
|||
|
# Adding the cache control header for js and css files
|
|||
|
# Make sure it is BELOW the PHP block
|
|||
|
location ~ \.(?:css|js|woff|svg|gif)$ {
|
|||
|
try_files $uri /index.php$uri$is_args$args;
|
|||
|
add_header Cache-Control "public, max-age=15778463";
|
|||
|
# Add headers to serve security related headers (It is intended to
|
|||
|
# have those duplicated to the ones above)
|
|||
|
# Before enabling Strict-Transport-Security headers please read into
|
|||
|
# this topic first.
|
|||
|
# add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
|
|||
|
#
|
|||
|
# WARNING: Only add the preload option once you read about
|
|||
|
# the consequences in https://hstspreload.org/. This option
|
|||
|
# will add the domain to a hardcoded list that is shipped
|
|||
|
# in all major browsers and getting removed from this list
|
|||
|
# could take several months.
|
|||
|
add_header X-Content-Type-Options nosniff;
|
|||
|
add_header X-XSS-Protection "1; mode=block";
|
|||
|
add_header X-Robots-Tag none;
|
|||
|
add_header X-Download-Options noopen;
|
|||
|
add_header X-Permitted-Cross-Domain-Policies none;
|
|||
|
# Optional: Don't log access to assets
|
|||
|
access_log off;
|
|||
|
}
|
|||
|
|
|||
|
location ~ \.(?:png|html|ttf|ico|jpg|jpeg)$ {
|
|||
|
try_files $uri /index.php$uri$is_args$args;
|
|||
|
# Optional: Don't log access to other assets
|
|||
|
access_log off;
|
|||
|
}
|
|||
|
}
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>IL faut modifier le fichier de configuration de nextcloud</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 'trusted_domains' =>
|
|||
|
array (
|
|||
|
0 => 'nc.xoyize.xyz',
|
|||
|
),
|
|||
|
'datadirectory' => '/var/www/nextcloud/data',
|
|||
|
'overwrite.cli.url' => 'https://nc.xoyize.xyz',
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vérification et relance nginx</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nginx -t
|
|||
|
systemctl restart nginx
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<div class="d-print-none"><footer class="article__footer"><meta itemprop="dateModified" content="2018-11-23T00:00:00+01: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/11/23/Debian-stretch-XFCE-Asus-eeepc1001ha.html">Debian 9 stretch 32bits + XFCE sur Asus eeepc 1001ha (noir)</a></div><div class="next"><span>SUIVANT</span><a href="/2018/11/23/FirstHeberg-KVM4-Debian9-serveur.html">FirstHeberg KVM4 Debian 9 serveur web + messagerie domaine xoyize.xyz (février 2018)</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>
|
|||
|
|