yannstatic/static/2018/11/23/KVM4-1-Debian-stretch-serveur-nginx-openssl-http2-php7-mariadb.html

2682 lines
220 KiB
HTML
Raw Normal View History

2024-10-31 20:18:37 +01:00
<!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>xoyize.xyz 1 Serveur Debian9 nginx letsencrypt php7 mariadb parefeu sauvegarde - YannStatic</title>
<meta name="description" content="KVM Debian 9 serveur (nginx+php7+mariadb)">
<link rel="canonical" href="https://static.rnmkcy.eu/2018/11/23/KVM4-1-Debian-stretch-serveur-nginx-openssl-http2-php7-mariadb.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}&nbsp;{title}</a></li>'
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a></li>'
})
</script>
<!-- Fin déclaration champ de recherche --></div><nav class="navigation">
2024-11-28 11:42:23 +01:00
<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="/syntaxe-markdown.html">Aide</a></li></ul>
2024-10-31 20:18:37 +01:00
</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;">xoyize.xyz 1 Serveur Debian9 nginx letsencrypt php7 mariadb parefeu sauvegarde</h1></header></div><meta itemprop="headline" content="xoyize.xyz 1 Serveur Debian9 nginx letsencrypt php7 mariadb parefeu sauvegarde"><div class="article__info clearfix"><ul class="left-col menu"><li>
2024-11-08 14:10:33 +01:00
<a class="button button--secondary button--pill button--sm" style="color:#00FFFF" href="/archive.html?tag=serveur">serveur</a>
2024-10-31 20:18:37 +01:00
</li></ul><ul class="right-col menu"><li>
<i class="far fa-calendar-alt"></i>&nbsp;<span title="Création" style="color:#FF00FF">23&nbsp;nov.&nbsp;&nbsp;2018</span>
<span title="Modification" style="color:#00FF7F">19&nbsp;févr.&nbsp;2018</span></li></ul></div><meta itemprop="datePublished" content="2018-02-19T00: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">&#8679;</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><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 : Lets 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 &amp;&amp; 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" &gt;&gt; /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 &gt;&gt; /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à lexpiration dun long délai dinactivité. Il existe un bogue 751636 à ce sujet. Pour linstant, la solution de contournement à ce problème est dinstaller :</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 quil 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/ &amp;&amp; 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 &amp;&amp; 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 à linstallation</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 à linstallation</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 dun 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" &gt;&gt; /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>; &lt;&lt;&gt;&gt; DiG 9.10.3-P4-Debian &lt;&lt;&gt;&gt; @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&gt; <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">&gt;&gt;</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> &lt; <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">&gt;</span> <span class="nv">$Tmp</span><span class="err">$</span>
diff <span class="nv">$TmpName</span> /var/lib/unbound/root.hints <span class="o">&gt;&gt;</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">&gt;&gt;</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">&gt;&gt;</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> &lt; <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 Lets 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
</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" &gt; /dev/null
</code></pre></div></div>
<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 dune autorité de certification (lets 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&amp;openssl=1.0.1t&amp;hsts=yes&amp;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&amp;openssl=1.0.1t&amp;hsts=yes&amp;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 quils 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 dautoriser 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 daccord sur un nombre (quils peuvent utiliser comme clé pour chiffrer la conversation suivante) sans quun 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
# &gt; 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 lexé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 derreur, redémarrez le serveur, les règles seront oubliées et vous permettront de reprendre la main.<br />
En revanche, si les tests savè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 &gt; /etc/iptables/rules.v4
ip6tables-save &gt; /etc/iptables/rules.v6
iptables-restore &lt; /etc/iptables/rules.v4
ip6tables-restore &lt; /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="debian-màj">Debian Màj</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 &gt; /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 ladresse 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>
</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>
&emsp;</div>
-->
</footer>
<div class="article__section-navigator clearfix"><div class="previous"><span>PRÉCÉDENT</span><a href="/2018/11/23/KVM-Debian-luxembourg.html">KVM Debian Stretch (Luxembourg)</a></div><div class="next"><span>SUIVANT</span><a href="/2018/11/23/KVM4-2-Debian-stretch-serveur-messagerie-postfix-dovecot-mariadb.html">xoyize.xyz 2 Serveur de messagerie (postfix dovecot dkim spf dmarc rainloop spamassassin clamav fail2ban)</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}&nbsp;{title}</a>&nbsp;(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>