yannstatic/static/2023/11/14/VPS-Time4_debian.html

2823 lines
244 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>TIME4VPS Debian 12 wireguard - YannStatic</title>
<meta name="description" content="Serveur wiregard + DNS unbound + gestion des clients peer wireguard">
<link rel="canonical" href="https://static.rnmkcy.eu/2023/11/14/VPS-Time4_debian.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;">TIME4VPS Debian 12 wireguard</h1></header></div><meta itemprop="headline" content="TIME4VPS Debian 12 wireguard"><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=wireguard">wireguard</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">14&nbsp;nov.&nbsp;&nbsp;2023</span>
<span title="Modification" style="color:#00FF7F">10&nbsp;janv.&nbsp;2024</span></li></ul></div><meta itemprop="datePublished" content="2024-01-10T00:00:00+01:00">
<meta itemprop="keywords" content="wireguard"><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><p><em>Serveur wiregard + DNS unbound + gestion des clients peer wireguard</em></p>
<p><img src="/images/time4vps-logo.png" alt="TIME4VPS" /> <em>fournisseur dhébergement Web en Lituanie <img src="/images/lt.png" alt="" /></em></p>
<p>Connexion sur lhébergeur TIME4VPS (zone client) : <a href="https://billing.time4vps.com/clientarea/">https://billing.time4vps.com/clientarea/</a><br />
<strong>Modifier hostname</strong> <br />
Cliquer sur <strong>Change Hostname</strong> et saisir <strong>xoyaz.xyz</strong> pour valider le <strong>reverse DNS</strong></p>
<ul>
<li><strong>Product/Service</strong> Linux VPS - Linux 8</li>
<li><strong>Label</strong> yann-time4vps</li>
<li><strong>Hostname</strong> xoyaz.xyz</li>
<li>OS: Debian 12 (64-bit)</li>
<li>Processor: 3 x 2.6 GHz</li>
<li>Memory: 8192 MB</li>
<li>Storage: 80 GB</li>
<li>Bandwidth: 100 Mbps (Monthly limit: 16 TB)</li>
</ul>
<h2 id="debian-12">Debian 12</h2>
<p><img src="/images/debian12-logo.png" alt="" height="30" /></p>
<p>PARAMETRES DACCES:<br />
Ladresse IPv4 du VPS est : 195.181.242.156<br />
Ladresse IPv6 du VPS est : 2a02:7b40:c3b5:f29c::/64</p>
<p>Le nom du VPS est : yann-time4vps<br />
Connexion SSH en “root” sans mot de passe</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh root@195.181.242.156
</code></pre></div></div>
<p>Mise à jour</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt update &amp;&amp; apt upgrade
</code></pre></div></div>
<p>Modifier mot de passe “root”</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>passwd
</code></pre></div></div>
<p>Réseau</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: ens3: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:00:c3:b5:f2:9c brd ff:ff:ff:ff:ff:ff
altname enp0s3
inet 195.181.242.156/32 brd 195.181.242.156 scope global ens3
valid_lft forever preferred_lft forever
inet 10.181.242.156/8 brd 10.255.255.255 scope global ens3:1
valid_lft forever preferred_lft forever
inet6 2a02:7b40:c3b5:f29c::1/128 scope global
valid_lft forever preferred_lft forever
inet6 fe80::200:c3ff:feb5:f29c/64 scope link
valid_lft forever preferred_lft forever
</code></pre></div></div>
<p>Noyau et OS : <code class="language-plaintext highlighter-rouge">uname -a</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Linux xoyaz.xyz 6.1.0-13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.55-1 (2023-09-29) x86_64 GNU/Linux
</code></pre></div></div>
<p>Paramétrage fuseau <strong>Europe/Paris</strong> : <code class="language-plaintext highlighter-rouge">dpkg-reconfigure tzdata</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Current default time zone: 'Europe/Paris'
Local time is now: Thu Nov 9 19:36:07 CET 2023.
Universal Time is now: Thu Nov 9 18:36:07 UTC 2023.
</code></pre></div></div>
<h3 id="création-utilisateur">Création utilisateur</h3>
<p>Utilisateur <strong>ian</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>useradd -m -d /home/ian/ -s /bin/bash ian
</code></pre></div></div>
<p>Mot de passe <strong>ian</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>passwd ian
</code></pre></div></div>
<p>Visudo pour les accès root via utilisateur <strong>ian</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install sudo
echo "ian ALL=(ALL) NOPASSWD: ALL" &gt;&gt; /etc/sudoers
</code></pre></div></div>
<h3 id="openssh-clé-et-script">OpenSSH, clé et script</h3>
<p><img src="/images/openssh-logo.png" alt="OpenSSH" width="80" /><br />
<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>time4vps</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/time4vps
</code></pre></div></div>
<p>Envoyer les clés publiques sur le serveur KVM</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh-copy-id -i ~/.ssh/time4vps.pub ian@195.181.242.156
</code></pre></div></div>
<p>On se connecte</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh ian@195.181.242.156
</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-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Port</span> <span class="m">55156</span>
<span class="n">PermitRootLogin</span> <span class="n">no</span>
<span class="n">PasswordAuthentication</span> <span class="n">no</span>
</code></pre></div></div>
<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 55156 -i ~/.ssh/time4vps ian@195.181.242.156
</code></pre></div></div>
<h3 id="outils-scripts-motd-et-ssh_rc_bash">Outils, scripts motd et ssh_rc_bash</h3>
<p>Installer utilitaires</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt install rsync curl tmux jq figlet git dnsutils tree -y
</code></pre></div></div>
<p>Motd</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo rm /etc/motd &amp;&amp; sudo nano /etc/motd
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> _____ ___ __ __ ___ _ _ __ __ ___ ___
|_ _||_ _|| \/ || __|| | |\ \ / /| _ \/ __|
| | | | | |\/| || _| |_ _|\ V / | _/\__ \
|_| |___||_| |_||___| |_| \_/ |_| |___/
___ _ _ _ ___
| \ ___ | |__ (_) __ _ _ _ / ||_ )
| |) |/ -_)| '_ \| |/ _` || ' \ | | / /
|___/ \___||_.__/|_|\__,_||_||_| |_|/___|
</code></pre></div></div>
<p>Script <strong>ssh_rc_bash</strong></p>
<blockquote>
<p><strong>ATTENTION!!! Les scripts sur connexion peuvent poser des problèmes pour des appels externes autres que ssh</strong></p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget https://static.xoyize.xyz/files/ssh_rc_bash
chmod +x ssh_rc_bash # rendre le bash exécutable
./ssh_rc_bash # exécution
</code></pre></div></div>
<p><img src="/images/xoyaz.png" alt="" /></p>
<p><strong>Historique de la ligne de commande</strong><br />
Ajoutez la recherche dhistorique de la ligne de commande au terminal.
Tapez un début de commande précédent, puis utilisez shift + up (flèche haut) pour rechercher lhistorique filtré avec le début de la commande.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Global, tout utilisateur
echo '"\e[1;2A": history-search-backward' | sudo tee -a /etc/inputrc
echo '"\e[1;2B": history-search-forward' | sudo tee -a /etc/inputrc
</code></pre></div></div>
<h3 id="hostname">Hostname</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hostnamectl
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Static hostname: xoyaz.xyz
Icon name: computer-vm
Chassis: vm 🖴
Machine ID: efeae6fcc90e4228a36d1b742685c11e
Boot ID: 4f7c7d9fc9c5442b80e9dd8a6e4f8b41
Virtualization: kvm
Operating System: Debian GNU/Linux 12 (bookworm)
Kernel: Linux 6.1.0-13-amd64
Architecture: x86-64
Hardware Vendor: Virtuozzo
Hardware Model: KVM
Firmware Version: 1.11.0-2.vz7.5
</code></pre></div></div>
<h3 id="domaine-xoyazxyz">Domaine xoyaz.xyz</h3>
<p><img src="/images/dns-logo.png" alt="dns" width="30" /> Zone dns OVH</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$TTL 3600
@ IN SOA dns106.ovh.net. tech.ovh.net. (2023110909 86400 3600 3600000 300)
IN NS ns106.ovh.net.
IN NS dns106.ovh.net.
IN A 195.181.242.156
IN AAAA 2a02:7b40:c3b5:f29c::1
IN CAA 128 issue "letsencrypt.org"
* IN A 195.181.242.156
* IN AAAA 2a02:7b40:c3b5:f29c::1
</code></pre></div></div>
<h3 id="certificats-letsencrypt">Certificats LetsEncrypt</h3>
<p><img src="/images/LetsEncrypt.png" alt="LetsEncrypt.png" width="100" /><br />
Installer acme: <a href="https://blog.cinay.xyz/2017/08/Acme-Certficats-Serveurs.html">Serveur , installer et renouveler les certificats SSL Lets encrypt via Acme</a></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> ~
<span class="nb">sudo </span>apt <span class="nb">install </span>socat <span class="nt">-y</span> <span class="c"># prérequis</span>
git clone https://github.com/acmesh-official/acme.sh.git
<span class="nb">cd </span>acme.sh
./acme.sh <span class="nt">--install</span> <span class="c"># se déconnecter pour prise en compte</span>
<span class="c"># export des clé API OVH</span>
</code></pre></div></div>
<p>Générer les certificats pour le domaine xoyaz.xyz</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> acme.sh --dns dns_ovh --server letsencrypt --issue --keylength ec-384 -d 'xoyaz.xyz' -d '*.xoyaz.xyz'
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Thu Nov 9 08:18:04 PM CET 2023] Your cert is in: /home/ian//.acme.sh/xoyaz.xyz_ecc/xoyaz.xyz.cer
[Thu Nov 9 08:18:04 PM CET 2023] Your cert key is in: /home/ian//.acme.sh/xoyaz.xyz_ecc/xoyaz.xyz.key
[Thu Nov 9 08:18:04 PM CET 2023] The intermediate CA cert is in: /home/ian//.acme.sh/xoyaz.xyz_ecc/ca.cer
[Thu Nov 9 08:18:04 PM CET 2023] And the full chain certs is there: /home/ian//.acme.sh/xoyaz.xyz_ecc/fullchain.cer
</code></pre></div></div>
<p>Installer les certificats</p>
<p>nous stockons les fichiers dans le répertoire /etc/ssl/private/ (qui doit être créé au préalable), la commande serait :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo mkdir</span> <span class="nt">-p</span> /etc/ssl/private/
<span class="nb">sudo chown</span> <span class="nv">$USER</span> <span class="nt">-R</span> /etc/ssl/private/
acme.sh <span class="nt">--ecc</span> <span class="nt">--install-cert</span> <span class="nt">-d</span> <span class="s1">'xoyaz.xyz'</span> <span class="nt">-d</span> <span class="s1">'*.xoyaz.xyz'</span> <span class="nt">--key-file</span> /etc/ssl/private/xoyaz.xyz-key.pem <span class="nt">--fullchain-file</span> /etc/ssl/private/xoyaz.xyz-fullchain.pem
<span class="c"># --reloadcmd 'sudo systemctl reload nginx.service'</span>
</code></pre></div></div>
<p>Renouvellement automatique</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>30 0 * * * "$HOME/.acme.sh"/acme.sh --cron --home "$HOME/.acme.sh" --renew-hook "$HOME/.acme.sh/acme.sh --ecc --install-cert -d 'xoyaz.xyz' --key-file /etc/ssl/private/xoyaz.xyz-key.pem --fullchain-file /etc/ssl/private/xoyaz.xyz-fullchain.pem" &gt; /dev/null
</code></pre></div></div>
<h3 id="parefeu">Parefeu</h3>
<p><img src="/images/ufw-logo-a.png" alt="ufw" width="50" /><br />
<em>UFW, ou pare - feu simple , est une interface pour gérer les règles de pare-feu dans Arch Linux, Debian ou Ubuntu. UFW est utilisé via la ligne de commande (bien quil dispose dinterfaces graphiques disponibles), et vise à rendre la configuration du pare-feu facile (ou simple).</em></p>
<p>Installation <strong>Debian / Ubuntu</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt install ufw
</code></pre></div></div>
<p><em>Par défaut, les jeux de règles dUFW sont vides, de sorte quil napplique aucune règle de pare-feu, même lorsque le démon est en cours dexécution.</em></p>
<p>Les règles</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo ufw allow 55156/tcp # port SSH , 55156
sudo ufw allow https # port 443
sudo ufw allow DNS # port 53
sudo ufw allow 51820/udp # wireguard
</code></pre></div></div>
<p>Activer le parefeu</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo ufw enable
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
</code></pre></div></div>
<p>Status</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> sudo ufw status verbose
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
55156/tcp ALLOW IN Anywhere
443 ALLOW IN Anywhere
53 (DNS) ALLOW IN Anywhere
51820/udp ALLOW IN Anywhere
55156/tcp (v6) ALLOW IN Anywhere (v6)
443 (v6) ALLOW IN Anywhere (v6)
53 (DNS (v6)) ALLOW IN Anywhere (v6)
51820/udp (v6) ALLOW IN Anywhere (v6)
</code></pre></div></div>
<h3 id="résolveur-unbound">Résolveur (Unbound)</h3>
<p><img src="/images/unbound-250.png" alt="" height="80" /><br />
Commençons par installer et configurer le résolveur DNS. Il existe plusieurs
logiciels pour faire de la résolution comme <a href="https://www.isc.org/bind">BIND 9</a>,
<a href="https://www.knot-resolver.cz">Knot Resolver</a> ou encore
<a href="https://nlnetlabs.nl/projects/unbound/about/">Unbound</a>. Nous avons choisi
dutiliser Unbound et cette partie documente comment installer et configuer ce
résolveur.</p>
<p>Prérequis, installer resolvconf</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install resolvconf
</code></pre></div></div>
<p>Le fichier cat /etc/resolv.conf</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
# 127.0.0.53 is the systemd-resolved stub resolver.
# run "resolvectl status" to see details about the actual nameservers.
nameserver 127.0.0.1
</code></pre></div></div>
<p>En mode su</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -s
</code></pre></div></div>
<p>Installation Unbound</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt <span class="nb">install </span>unbound
</code></pre></div></div>
<p>Téléchargement de la liste des serveurs DNS racines</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-o</span> /var/lib/unbound/root.hints https://www.internic.net/domain/named.cache
<span class="nb">chown </span>unbound:unbound /var/lib/unbound/root.hints
</code></pre></div></div>
<p>Créer le fichier de configuration <code class="language-plaintext highlighter-rouge">/etc/unbound/unbound.conf.d/unbound-ian.conf</code> en tenant compte des adresses privées</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server:
# ne rien enregistrer dans les journaux hormis les erreurs
verbosity: 0
# n'écouter que sur l'interface locale en IPv4
# unbound nécessite d'être relancé si modifié
interface: 127.0.0.1
port: 53
# refuser tout le monde sauf les connexions locales (pas forcément
# nécessaire vu que le serveur n'écoute que sur la boucle locale en IPv4)
access-control: 0.0.0.0/0 refuse
access-control: 127.0.0.1/32 allow
# par défaut, unbound ne log pas les requêtes ni les réponses
# on peut le rappeler au cas où
log-queries: no
log-replies: no
# imposer la QNAME minimisation (RFC 7816)
# Pour mieux protéger la vie privée
qname-minimisation: yes
# même si le serveur faisant autorité ne le veut pas
# après discussion, il est possible que cette option ne soit
# pas recommandée dans le cadre d'un résolveur ouvert
qname-minimisation-strict: yes
</code></pre></div></div>
<p>Vérifier la validité du fichier de configuration avec la commande
suivante :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>unbound-checkconf /etc/unbound/unbound.conf.d/unbound-ian.conf
</code></pre></div></div>
<p><em>unbound-checkconf: no errors in /etc/unbound/unbound.conf.d/unbound-ian.conf</em></p>
<p>Toutes les règles disponibles sont détaillées dans le manuel <code class="language-plaintext highlighter-rouge">man 5 unbound.conf</code> ou <a href="https://nlnetlabs.nl/documentation/unbound/unbound.conf/">dans le manuel en ligne</a>.</p>
<p>Modifier le fichier /etc/resolv.conf</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/resolv.conf
</code></pre></div></div>
<p>nameserver 127.0.0.1</p>
<p>Relancer le serveur unbound</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl restart unbound
</code></pre></div></div>
<p>Sassurer que tout fonctionne bien à laide de la commande
<code class="language-plaintext highlighter-rouge">dig</code> disponible dans le paquet <code class="language-plaintext highlighter-rouge">bind9-dnsutils</code> ou <code class="language-plaintext highlighter-rouge">dnsutils</code>. Pour cela il
suffit de spécifier ladresse de notre résolveur, ici <code class="language-plaintext highlighter-rouge">127.0.0.1</code> ou <code class="language-plaintext highlighter-rouge">::1</code> et
deffectuer une requête DNS. Ici on demande à Unbound de récupérer
lenregistrement <code class="language-plaintext highlighter-rouge">AAAA</code> associé au nom de domaine <code class="language-plaintext highlighter-rouge">afnic.fr</code>.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dig @127.0.0.1 AAAA afnic.fr
</code></pre></div></div>
<p>Résultat commande</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>; &lt;&lt;&gt;&gt; DiG 9.18.19-1~deb12u1-Debian &lt;&lt;&gt;&gt; @127.0.0.1 AAAA afnic.fr
; (1 server found)
;; global options: +cmd
;; Got answer:
;; -&gt;&gt;HEADER&lt;&lt;- opcode: QUERY, status: NOERROR, id: 40492
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;afnic.fr. IN AAAA
;; ANSWER SECTION:
afnic.fr. 1800 IN AAAA 2001:67c:2218:302::51:231
;; Query time: 816 msec
;; SERVER: 127.0.0.1#53(127.0.0.1) (UDP)
;; WHEN: Mon Nov 13 14:11:42 CET 2023
;; MSG SIZE rcvd: 65
</code></pre></div></div>
<p>Une réponse est bien renvoyée. Le résolveur fonctionne.Vérifier que tout est opérationnel en IPv4, et en utilisant UDP et TCP.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ dig +notcp @127.0.0.1 AAAA afnic.fr # connexion UDP en IPv4 au résolveur
$ dig +tcp @127.0.0.1 AAAA afnic.fr # connexion TCP en IPv4 au résolveur
</code></pre></div></div>
<p class="info">À ce stade, un résolveur Unbound est configuré en local et écoute sur le port
<code class="language-plaintext highlighter-rouge">53</code>. Il peut donc être utilisé pour résoudre toutes les requêtes en provenance
de la machine.</p>
<h3 id="envoi-de-message-postfix">Envoi de message (postfix)</h3>
<ul>
<li><a href="/2022/08/27/Debian_Postfix_serveur_SMTP_envoi_uniquement.html">Envoi de message - Installer et configurer Postfix comme serveur SMTP denvoi uniquement</a></li>
</ul>
<h2 id="wireguard">Wireguard</h2>
<p><img src="/images/wireguard_icon.png" alt="wireguard" width="100" /></p>
<ul>
<li><a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-wireguard-on-ubuntu-20-04">How To Set Up WireGuard on Ubuntu 20.04</a></li>
<li><a href="https://blog.ruanbekker.com/blog/2021/03/10/wireguard-vpn-with-unbound-ads-blocking-dns/">Wireguard VPN With Unbound ADS Blocking DNS</a></li>
</ul>
<h3 id="installation-sur-le-serveur">Installation sur le serveur</h3>
<p>Installer WireGuard sur le serveur</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt install wireguard
</code></pre></div></div>
<h3 id="générer-jeu-de-clés-privée-et-publique">Générer jeu de clés privée et publique</h3>
<p>Générer une jeu de clés privée et publique pour le serveur</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wg genkey | sudo tee /etc/wireguard/private.key
sudo chmod go= /etc/wireguard/private.key
</code></pre></div></div>
<p>La commande <code class="language-plaintext highlighter-rouge">sudo chmod go=…</code> supprime toutes les autorisations sur le fichier pour les utilisateurs et les groupes autres que lutilisateur root pour garantir que lui seul peut accéder à la clé privée.<br />
Vous devriez recevoir une seule ligne de sortie codée base64, qui est la clé privée. Une copie de la sortie est également stockée dans le fichier /etc/wireguard/private.key pour référence future par la partie de la commande tee. <u>Notez soigneusement la clé privée générée</u>, car vous devrez lajouter au fichier de configuration de WireGuard plus loin dans cette section.</p>
<p>Utilisez la commande suivante pour créer le fichier de clé publique </p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo cat /etc/wireguard/private.key | wg pubkey | sudo tee /etc/wireguard/public.key
</code></pre></div></div>
<h3 id="adresses-ip-privées">Adresses IP privées</h3>
<p>Les plages dadresses privées utilisées</p>
<p>IPV4 : 10.22.1.0/24</p>
<p>Pour la plage IPV6, il faut la générer à partir de lhorodatage qui correspond au nombre de secondes (le %s dans la commande date) et de nanosecondes (le %N) depuis le 1970-01-01 00:00:00 UTC combinés</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>date +%s%N
</code></pre></div></div>
<p>Résultat: 1699963776550192068</p>
<p>Ensuite, copiez la valeur machine-id de votre serveur à partir du fichier. /var/lib/dbus/machine-id</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cat /var/lib/dbus/machine-id
</code></pre></div></div>
<p>Résultat: 75971165a9bd46a6814eeb1061620d63</p>
<p>Combiner lhorodatage avec machine-id et hacher la valeur résultante à laide de lalgorithme SHA-1</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>printf 169996377655019206875971165a9bd46a6814eeb1061620d63 | sha1sum
</code></pre></div></div>
<p>Résultat: c4548daf445749b5455a2568d786c4dba8d37f3a -</p>
<p>Lalgorithme de la RFC ne nécessite que les 40 bits les moins significatifs (de fin), ou 5 octets, de la sortie hachée. Utilisez la commande cut pour imprimer les 5 derniers octets codés en hexadécimal du hachage</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>printf c4548daf445749b5455a2568d786c4dba8d37f3a | cut -c 31-
</code></pre></div></div>
<p>Largument -c indique à la commande cut de sélectionner uniquement un jeu de caractères spécifié. L argument 31- indique à cut dimprimer tous les caractères de la position 31 jusquà la fin de la ligne de saisie.</p>
<p>Résultat: dba8d37f3a soit db a8 d3 7f 3a</p>
<p>Vous pouvez maintenant créer votre préfixe réseau IPv6 unique en ajoutant les 5 octets que vous avez générés avec le préfixe <code class="language-plaintext highlighter-rouge">fd</code>, en séparant tous les 2 octets par deux points <code class="language-plaintext highlighter-rouge">:</code> pour plus de lisibilité. Étant donné que chaque sous-réseau de votre préfixe unique peut contenir un total de 18 446 744 073 709 551 616 adresses IPv6 possibles, vous pouvez limiter le sous-réseau à une taille standard de /64 pour plus de simplicité.
En utilisant les octets générés précédemment avec la taille /64 du sous-réseau, le préfixe résultant sera le suivant</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Unique Local IPv6 Address Prefix
fddb:a8d3:7f3a::/64
</code></pre></div></div>
<p>Cette plage <code class="language-plaintext highlighter-rouge">fddb:a8d3:7f3a::/64</code> est ce que vous utiliserez pour attribuer des adresses IP individuelles à vos interfaces de tunnel WireGuard sur le serveur et les « peers ». Pour attribuer une IP au serveur, ajoutez un <code class="language-plaintext highlighter-rouge">1</code> après les derniers caractères <code class="language-plaintext highlighter-rouge">::</code>. Ladresse résultante sera <code class="language-plaintext highlighter-rouge">fddb:a8d3:7f3a::1/64</code></p>
<p>Les « Peers » peuvent utiliser nimporte quelle adresse de la plage IP, mais vous incrémentez généralement la valeur de un à chaque fois que vous ajoutez un « Peers », par exemple <code class="language-plaintext highlighter-rouge">fddb:a8d3:7f3a::2/64</code></p>
<h3 id="configuration-du-serveur-wireguard">Configuration du serveur WireGuard</h3>
<p>Avant de créer la configuration de votre serveur WireGuard,</p>
<ol>
<li>Vous disposez de la clé privée</li>
<li>WireGuard avec IPv4, adresse IP : <code class="language-plaintext highlighter-rouge">10.22.1.1/24</code></li>
<li>WireGuard avec IPv6, adresse IP : <code class="language-plaintext highlighter-rouge">fddb:a8d3:7f3a::1/64</code></li>
</ol>
<p>Une fois que vous disposez de la clé privée et des adresses IP requises, créez un nouveau fichier de configuration à laide de votre éditeur préféré nano en exécutant la commande suivante</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/wireguard/wg0.conf
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Interface]
PrivateKey = base64_encoded_private_key_goes_here
Address = 10.22.1.1/24, fddb:a8d3:7f3a::1/64
ListenPort = 51820
SaveConfig = true
</code></pre></div></div>
<p>base64_encoded_private_key_goes_here est égal au contenu du fichier <code class="language-plaintext highlighter-rouge">/etc/wireguard/private.key</code><br />
La ligne <code class="language-plaintext highlighter-rouge">SaveConfig</code> garantit que lorsquune interface WireGuard est arrêtée, toutes les modifications seront enregistrées dans le fichier de configuration.</p>
<h3 id="ajustement-configuration-réseau-serveur-wireguard">Ajustement configuration réseau serveur WireGuard</h3>
<p>Si vous souhaitez acheminer le trafic Internet de votre homologue WireGuard via le serveur WireGuard, vous devrez alors configurer le transfert IP en suivant cette section du didacticiel.
Pour configurer le transfert, ouvrez le fichier <code class="language-plaintext highlighter-rouge">/etc/sysctl.conf</code> à laide de nano ou de votre éditeur préféré :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/sysctl.conf
</code></pre></div></div>
<p>Si vous utilisez IPv4 avec WireGuard, ajoutez la ligne suivante en bas du fichier</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>net.ipv4.ip_forward=1
</code></pre></div></div>
<p>Si vous utilisez IPv6 avec WireGuard, ajoutez cette ligne en bas du fichier</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>net.ipv6.conf.all.forwarding=1
</code></pre></div></div>
<p>Si vous utilisez à la fois IPv4 et IPv6, assurez-vous dinclure les deux lignes. Enregistrez et fermez le fichier lorsque vous avez terminé.<br />
Pour lire le fichier et charger les nouvelles valeurs pour votre session de terminal en cours, exécutez :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo sysctl -p
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>net.ipv6.conf.all.forwarding = 1
net.ipv4.ip_forward = 1
</code></pre></div></div>
<p>Votre serveur WireGuard sera désormais capable de transférer le trafic entrant du périphérique Ethernet VPN virtuel vers dautres sur le serveur, et de là vers lInternet public. Lutilisation de cette configuration vous permettra dacheminer tout le trafic Web de votre WireGuard Peer via ladresse IP de votre serveur, et ladresse IP publique de votre client sera effectivement masquée.</p>
<h3 id="configuration-pare-feu-serveur-wireguard">Configuration pare-feu serveur WireGuard</h3>
<p><em>modifier la configuration du serveur WireGuard pour ajouter des règles de pare-feu qui garantiront que le trafic vers et depuis le serveur et les clients est correctement acheminé</em></p>
<p>Cependant, avant que le trafic puisse être acheminé correctement via votre serveur, vous devrez configurer certaines règles de pare-feu. Ces règles garantiront que le trafic vers et depuis votre serveur WireGuard et vos « peers » circulent correctement.</p>
<p>Pour autoriser le trafic VPN WireGuard à travers le pare-feu du serveur, vous devrez activer le masquage, qui est un concept iptables qui fournit une traduction dadresses réseau (NAT) dynamique à la volée pour acheminer correctement les connexions client.<br />
Recherchez dabord linterface réseau publique de votre serveur WireGuard à laide de la sous-commande <code class="language-plaintext highlighter-rouge">ip route</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip route list default
</code></pre></div></div>
<p>Linterface publique est la chaîne trouvée dans la sortie de cette commande qui suit le mot « dev ». Par exemple, ce résultat montre linterface nommée ens3, qui est mise en évidence ci-dessous</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>default via 169.254.0.1 dev ens3
</code></pre></div></div>
<p>Pour ajouter des règles de pare-feu à votre serveur WireGuard, ouvrir à nouveau le fichier <code class="language-plaintext highlighter-rouge">/etc/wireguard/wg0.conf</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/wireguard/wg0.conf
</code></pre></div></div>
<p>En bas du fichier après la ligne <code class="language-plaintext highlighter-rouge">SaveConfig = true</code>, collez les lignes suivantes</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PostUp = ufw route allow in on wg0 out on ens3
PostUp = iptables -t nat -I POSTROUTING -o ens3 -j MASQUERADE
PostUp = ip6tables -t nat -I POSTROUTING -o ens3 -j MASQUERADE
PreDown = ufw route delete allow in on wg0 out on ens3
PreDown = iptables -t nat -D POSTROUTING -o ens3 -j MASQUERADE
PreDown = ip6tables -t nat -D POSTROUTING -o ens3 -j MASQUERADE
</code></pre></div></div>
<p>Les lignes <code class="language-plaintext highlighter-rouge">PostUp</code> sexécuteront lorsque le serveur WireGuard démarrera le tunnel VPN virtuel. Dans lexemple ici, il en ajoutera trois règles  <code class="language-plaintext highlighter-rouge">ufw</code> et <code class="language-plaintext highlighter-rouge">iptables</code></p>
<ol>
<li><code class="language-plaintext highlighter-rouge">ufw route allow in on wg0 out on ens3</code> - Cette règle permettra de transférer le trafic IPv4 et IPv6 entrant sur linterface VPN wg0 vers linterface réseau ens3 du serveur. Il fonctionne conjointement avec les valeurs sysctl <code class="language-plaintext highlighter-rouge">net.ipv4.ip_forward</code> et <code class="language-plaintext highlighter-rouge">net.ipv6.conf.all.forwarding</code> que vous avez configurées dans la section précédente.</li>
<li><code class="language-plaintext highlighter-rouge">iptables -t nat -I POSTROUTING -o ens3 -j MASQUERADE</code> - Cette règle configure le masquage et réécrit le trafic IPv4 entrant sur linterface VPN wg0 pour le faire apparaître comme sil provenait directement de ladresse IPv4 publique du serveur WireGuard.</li>
<li><code class="language-plaintext highlighter-rouge">ip6tables -t nat -I POSTROUTING -o ens3 -j MASQUERADE</code> - Cette règle configure le masquage et réécrit le trafic IPv6 entrant sur linterface VPN wg0 pour le faire apparaître comme sil provenait directement de ladresse IPv6 publique du serveur WireGuard.</li>
</ol>
<p>Les règles <code class="language-plaintext highlighter-rouge">PreDown</code> sexécutent lorsque le serveur WireGuard arrête le tunnel VPN virtuel. Ces règles sont linverse des règles <code class="language-plaintext highlighter-rouge">PostUp</code> et fonctionnent pour annuler les règles de transfert et de masquage de linterface VPN lorsque le VPN est arrêté.<br />
Dans les deux cas, modifiez la configuration pour inclure ou exclure les règles IPv4 et IPv6 appropriées à votre VPN. Par exemple, si vous utilisez uniquement IPv4, vous pouvez exclure les lignes avec les commandes ip6tables.
À linverse, si vous utilisez uniquement IPv6, modifiez la configuration pour inclure uniquement les commandes ip6tables. Les lignes ufw doivent exister pour toute combinaison de réseaux IPv4 et IPv6.</p>
<p>La dernière partie de la configuration du pare-feu sur votre serveur WireGuard consiste à autoriser le trafic vers et depuis le port WireGuard UDP lui-même. Si vous navez pas modifié le port dans le fichier /etc/wireguard/wg0.conf du serveur, le port que vous ouvrirez est 51820. Si vous avez choisi un port différent lors de la modification de la configuration, assurez-vous de le remplacer dans la commande UFW suivante.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo ufw allow 51820/udp
</code></pre></div></div>
<p>Après avoir ajouté ces règles, désactivez et réactivez UFW pour le redémarrer et charger les modifications de tous les fichiers que vous avez modifiés :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo ufw disable
sudo ufw enable
</code></pre></div></div>
<h3 id="démarrage-du-serveur-wireguard">Démarrage du serveur WireGuard</h3>
<p>WireGuard peut être configuré pour fonctionner comme un service systemd en utilisant son script wg-quick intégré. Bien que vous puissiez utiliser manuellement la commande wg pour créer le tunnel à chaque fois que vous voulez utiliser le VPN, cest un processus manuel qui devient répétitif et sujet aux erreurs. Au lieu de cela, vous pouvez utiliser systemctl pour gérer le tunnel avec laide du script <code class="language-plaintext highlighter-rouge">wg-quick</code>.</p>
<p>Le fichier complet de configuration wireguard à ce stade</p>
<p><img src="/images/wg_xoyaz01.png" alt="" /></p>
<p>Lutilisation dun service systemd signifie que vous pouvez configurer WireGuard pour quil démarre au démarrage afin que vous puissiez vous connecter à votre VPN à tout moment tant que le serveur est en cours dexécution. Pour ce faire, activer et démarrer le service <code class="language-plaintext highlighter-rouge">wg-quick</code> pour le tunnel <code class="language-plaintext highlighter-rouge">wg0</code> que vous avez défini en lajoutant à systemctl</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl enable wg-quick@wg0.service --now
</code></pre></div></div>
<p class="info">Notez que la commande spécifie le nom du périphérique <strong>wg0</strong> du tunnel en tant que partie du nom du service. Ce nom correspond au fichier de configuration <strong>/etc/wireguard/.conf</strong>. Cette approche du nommage signifie que vous pouvez créer autant de tunnels VPN distincts que vous le souhaitez à laide de votre serveur.</p>
<p>Par exemple, vous pourriez avoir un périphérique de tunnel et un nom de <strong>prod</strong> et son fichier de configuration serait <strong>/etc/wireguard/prod.conf</strong>. Chaque configuration de tunnel peut contenir différents paramètres IPv4, IPv6 et de pare-feu client. De cette manière, vous pouvez prendre en charge plusieurs connexions de pairs différentes, chacune avec ses propres adresses IP et règles de routage.</p>
<p>Vérifiez que le service WireGuard est actif avec la commande suivante. Vous devriez voir actif (running) dans la sortie</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl status wg-quick@wg0.service
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>● wg-quick@wg0.service - WireGuard via wg-quick(8) for wg0
Loaded: loaded (/lib/systemd/system/wg-quick@.service; enabled; preset: enabled)
Active: active (exited) since Tue 2023-11-14 13:40:00 CET; 9s ago
Docs: man:wg-quick(8)
man:wg(8)
https://www.wireguard.com/
https://www.wireguard.com/quickstart/
https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8
https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8
Process: 12551 ExecStart=/usr/bin/wg-quick up wg0 (code=exited, status=0/SUCCESS)
Main PID: 12551 (code=exited, status=0/SUCCESS)
CPU: 171ms
Nov 14 13:40:00 xoyaz.xyz wg-quick[12551]: [#] wg setconf wg0 /dev/fd/63
Nov 14 13:40:00 xoyaz.xyz wg-quick[12551]: [#] ip -4 address add 10.22.1.1/24 dev wg0
Nov 14 13:40:00 xoyaz.xyz wg-quick[12551]: [#] ip -6 address add fddb:a8d3:7f3a::1/64 dev wg0
Nov 14 13:40:00 xoyaz.xyz wg-quick[12551]: [#] ip link set mtu 1420 up dev wg0
Nov 14 13:40:00 xoyaz.xyz wg-quick[12551]: [#] ufw route allow in on wg0 out on ens3
Nov 14 13:40:00 xoyaz.xyz wg-quick[12584]: Rule added
Nov 14 13:40:00 xoyaz.xyz wg-quick[12584]: Rule added (v6)
Nov 14 13:40:00 xoyaz.xyz wg-quick[12551]: [#] iptables -t nat -I POSTROUTING -o ens3 -j MASQUERADE
Nov 14 13:40:00 xoyaz.xyz wg-quick[12551]: [#] ip6tables -t nat -I POSTROUTING -o ens3 -j MASQUERA&gt;
Nov 14 13:40:00 xoyaz.xyz systemd[1]: Finished wg-quick@wg0.service - WireGuard via wg-quick(8) fo&gt;
</code></pre></div></div>
<p>La sortie montre les commandes ip qui sont utilisées pour créer le périphérique virtuel wg0 et lui attribuer les adresses IPv4 et IPv6 que vous avez ajoutées au fichier de configuration. Vous pouvez utiliser ces règles pour dépanner le tunnel, ou avec la commande wg elle-même si vous souhaitez essayer de configurer manuellement linterface VPN.</p>
<p class="info">Une fois le serveur configuré et en marche, létape suivante consiste à configurer votre machine cliente en tant que WireGuard Peer et à se connecter au serveur WireGuard.</p>
<h2 id="clients-wireguard-peer">Clients Wireguard Peer</h2>
<p>On se positionne dans le dossier <strong>/etc/wireguard/</strong> du serveur en mode su</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -s
cd /etc/wireguard
</code></pre></div></div>
<h3 id="générer-des-clés-pour-chaque-peer">Générer des clés pour chaque “peer”</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">umask </span>077<span class="p">;</span> wg genkey | <span class="nb">tee </span>peer_A.key | wg pubkey <span class="o">&gt;</span> peer_A.pub
<span class="nb">umask </span>077<span class="p">;</span> wg genkey | <span class="nb">tee </span>peer_B.key | wg pubkey <span class="o">&gt;</span> peer_B.pub
<span class="nb">umask </span>077<span class="p">;</span> wg genkey | <span class="nb">tee </span>peer_C.key | wg pubkey <span class="o">&gt;</span> peer_C.pub
</code></pre></div></div>
<p>umask, les valeurs des autorisations en octal :</p>
<ul>
<li>0: Lire, écrire et exécuter</li>
<li>1: Lire et écrire</li>
<li>2: Lire et exécuter</li>
<li>3: Lire uniquement</li>
<li>4: Écrire et exécuter</li>
<li>5: Écrire uniquement</li>
<li>6: Exécuter uniquement</li>
<li>7: Aucune autorisation</li>
</ul>
<p>notre umask est défini sur 077</p>
<table>
<thead>
<tr>
<th style="text-align: left">Bit</th>
<th style="text-align: left">Cible</th>
<th style="text-align: left">Permissions sur les fichiers</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">0</td>
<td style="text-align: left">Propriétaires</td>
<td style="text-align: left">Lire, écrire et exécuter</td>
</tr>
<tr>
<td style="text-align: left">7</td>
<td style="text-align: left">Groupe</td>
<td style="text-align: left">Pas de permissions</td>
</tr>
<tr>
<td style="text-align: left">7</td>
<td style="text-align: left">Autres</td>
<td style="text-align: left">Pas de permissions</td>
</tr>
</tbody>
</table>
<h3 id="création-du-fichier-de-configuration-du-peer-wireguard">Création du fichier de configuration du Peer WireGuard</h3>
<p>Maintenant que vous avez une paire de clés et une clé preshared , vous pouvez créer un fichier de configuration pour chaque pair qui contient toutes les informations dont il a besoin pour établir une connexion au serveur WireGuard.</p>
<p>Vous aurez besoin de quelques informations pour le fichier de configuration :</p>
<ul>
<li>La clé privée encodée en base64 que vous avez générée sur le pair (contenu fichier /etc/wireguard/peer_A.key)</li>
<li>Les plages dadresses IPv4 et IPv6 que vous avez définies sur le serveur WireGuard (10.22.1.0/24 et fddb:a8d3:7f3a::/64)</li>
<li>La clé publique encodée en base64 du serveur WireGuard (contenu fichier /etc/wireguard/public.key)</li>
<li>La clé preshared pour le peer (contenu fichier /etc/wireguard/preshared_A.psk)</li>
<li>Ladresse IP publique et le numéro de port du serveur WireGuard. Habituellement, il sagit de ladresse IPv4, mais si votre serveur a une adresse IPv6 et que votre machine cliente a une connexion IPv6 à Internet, vous pouvez lutiliser à la place de ladresse IPv4.</li>
</ul>
<p>Avec toutes ces informations en main, ouvrez un nouveau fichier /etc/wireguard/peer_A.conf sur la machine WireGuard Peer en utilisant nano ou votre éditeur préféré :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/wireguard/peer_A.conf
</code></pre></div></div>
<p>Ajouter les lignes suivantes au fichier, en remplaçant les différentes données dans les sections surlignées, selon les besoins</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Interface]
PrivateKey = base64_encoded_peer_private_key (contenu de /etc/wireguard/peer_A.key, clé privée PEER A)
Address = 10.22.1.2/24
Address = fddb:a8d3:7f3a::2/64
[Peer]
PublicKey = base64_encoded_peer_public_key (contenu de /etc/wireguard/public.key, clé publique du serveur)
# AllowedIPs = 10.22.1.0/24, fddb:a8d3:7f3a::/64
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = IPV4_serveur:51820
</code></pre></div></div>
<p>Si vous souhaitez envoyer tout le trafic de votre pair via le VPN et utiliser le serveur WireGuard comme passerelle pour tout le trafic, vous pouvez utiliser 0.0.0.0/0, qui représente lensemble de lespace dadressage IPv4, et ::/0 pour lensemble de lespace dadressage IPv6.</p>
<p>Si vous utilisez le serveur WireGuard comme passerelle VPN pour tout le trafic de votre pair, vous devrez ajouter une ligne à la section <code class="language-plaintext highlighter-rouge">[Interface]</code> qui spécifie les résolveurs DNS. Si vous najoutez pas ce paramètre, vos requêtes DNS pourraient ne pas être sécurisées par le VPN, ou elles pourraient être révélées à votre fournisseur daccès Internet ou à dautres tiers.</p>
<p>Avant la ligne <code class="language-plaintext highlighter-rouge">[Peer]</code>, ajouter ce qui suit</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DNS = 10.22.1.1, fddb:a8d3:7f3a::1
[Peer]
</code></pre></div></div>
<p>Au final, vous disposez dun fichier de configuration <code class="language-plaintext highlighter-rouge">peer_A.conf</code> qui sera renommé <code class="language-plaintext highlighter-rouge">wg0.conf</code> à déposer dans le dossier /etc/wireguard/ du client sur lequel vous aurez prélablement installé wireguard</p>
<p>Le fichier complet de configuration wireguard du client PEER A pour une utilisation avec accès internet</p>
<p><img src="/images/wg_xoyaz02.png" alt="" /></p>
<p>Répéter la même opérations pour les PEER B et C</p>
<h3 id="ajout-de-la-clé-publique-du-peer-au-serveur-wireguard">Ajout de la clé publique du Peer au serveur WireGuard</h3>
<p>Avant de connecter le peer au serveur, il est important dajouter la clé publique du peer au serveur WireGuard. Cette étape garantit que vous serez en mesure de vous connecter et dacheminer le trafic sur le VPN. Sans cette étape, le serveur WireGuard ne permettra pas à lhomologue denvoyer ou de recevoir du trafic sur le tunnel.</p>
<p>Assurez-vous que vous avez une copie de la clé publique encodée en base64 pour le client Peer WireGuard</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo cat /etc/wireguard/public.pub
</code></pre></div></div>
<p>Sur le serveur WireGuard et exécutez la commande suivante</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo wg set wg0 peer PeURxj4Q75RaVhBKkRTpNsBPiPSGb5oQijgJsTa29hg= allowed-ips 10.22.1.2,fddb:a8d3:7f3a::2
</code></pre></div></div>
<p>Si le fichier client PEER est sur le serveur</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo</span> <span class="nt">-s</span>
wg <span class="nb">set </span>wg0 peer <span class="si">$(</span><span class="nb">cat</span> /etc/wireguard/peer_A.pub<span class="si">)</span> allowed-ips <span class="s1">'10.22.1.2,fddb:a8d3:7f3a::2'</span>
</code></pre></div></div>
<p>Notez que la partie <code class="language-plaintext highlighter-rouge">allowed-ips</code> de la commande prend une liste dadresses IPv4 et IPv6 séparées par des virgules. Vous pouvez spécifier des adresses IP individuelles si vous souhaitez restreindre ladresse IP quun homologue peut sattribuer, ou une plage comme dans lexemple si vos homologues peuvent utiliser nimporte quelle adresse IP dans la plage VPN. Notez également que deux homologues ne peuvent pas avoir le même paramètre dadresses IP autorisées.</p>
<p>Si vous souhaitez mettre à jour les adresses IP autorisées pour un client PEER existant, vous pouvez exécuter la même commande à nouveau, mais en changeant les adresses IP. Les adresses IP multiples sont supportées. Par exemple, pour modifier le Peer WireGuard que vous venez dajouter pour ajouter une IP comme 10.8.0.100 aux IP existantes 10.8.0.2 et fd0d:86fa:c3bc::2, vous devez exécuter ce qui suit :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo wg set wg0 peer PeURxj4Q75RaVhBKkRTpNsBPiPSGb5oQijgJsTa29hg= allowed-ips 10.22.1.2,10.22.1.100,fd0d:86fa:c3bc::2
</code></pre></div></div>
<p>Une fois que vous avez exécuté la commande pour ajouter le pair, vérifiez létat du tunnel sur le serveur à laide de la commande wg :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo wg
</code></pre></div></div>
<p><img src="/images/wg_xoyaz03.png" alt="" /></p>
<p>Notez que la ligne peer montre la clé publique du WireGuard Peer, et les adresses IP, ou les plages dadresses quil est autorisé à utiliser pour sassigner une IP.</p>
<p>Répéter la même opérations pour les PEER B et C</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo</span> <span class="nt">-s</span>
wg <span class="nb">set </span>wg0 peer <span class="si">$(</span><span class="nb">cat</span> /etc/wireguard/peer_B.pub<span class="si">)</span> allowed-ips <span class="s1">'10.22.1.3,fddb:a8d3:7f3a::3'</span>
wg <span class="nb">set </span>wg0 peer <span class="si">$(</span><span class="nb">cat</span> /etc/wireguard/peer_C.pub<span class="si">)</span> allowed-ips <span class="s1">'10.22.1.4,fddb:a8d3:7f3a::4'</span>
</code></pre></div></div>
<p>Une fois que vous avez exécuté la commande pour ajouter les clients PEER, vérifiez létat du tunnel sur le serveur à laide de la commande wg :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo wg
</code></pre></div></div>
<p><img src="/images/wg_xoyaz04.png" alt="" /></p>
<p>Maintenant que vous avez défini les paramètres de connexion du Peer sur le serveur, létape suivante consiste à démarrer le tunnel sur le Peer.</p>
<h3 id="actualiser-le-dns-unbound">Actualiser le DNS unbound</h3>
<p>Il faut tenit compte des adresses privées du vpn wireguard</p>
<p>Modifier le fichier de configuration <code class="language-plaintext highlighter-rouge">/etc/unbound/unbound.conf.d/unbound-ian.conf</code><br />
Ajouter les lignes suivantes</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> interface: 10.22.1.1
interface: fddb:a8d3:7f3a::1
access-control: 10.22.1.0/16 allow
access-control: fddb:a8d3:7f3a::/48 allow
</code></pre></div></div>
<p>Relancer DNS unbound</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl restart unbound
</code></pre></div></div>
<p>le fichier <code class="language-plaintext highlighter-rouge">/etc/unbound/unbound.conf.d/unbound-ian.conf</code> au final</p>
<p><img src="/images/wg_xoyaz11.png" alt="" /></p>
<h3 id="connexion-du-client-peer-wireguard-au-tunnel">Connexion du client Peer WireGuard au tunnel</h3>
<p><code class="language-plaintext info highlighter-rouge">Si vos fichiers PEER sont le serveur, il convient de les dupliquer sur le client dans un fichier /etc/wireguard/wg0.conf</code></p>
<p>Maintenant que votre serveur et votre pair sont tous deux configurés pour supporter votre choix dIPv4, IPv6, le transfert de paquets, et la résolution DNS, il est temps de connecter le pair au tunnel VPN.</p>
<p>Puisque vous ne souhaitez que le VPN soit activé pour certains cas dutilisation, nous utiliserons la commande wg-quick pour établir la connexion manuellement. Si vous souhaitez automatiser le démarrage du tunnel comme vous lavez fait sur le serveur, suivez les étapes de la section <em>Démarrage du serveur WireGuard</em> au lieu dutiliser la commande wq-quick.</p>
<p>Dans le cas où vous acheminez tout le trafic à travers le VPN et que vous avez configuré la redirection DNS, vous devrez installer lutilitaire resolvconf sur le client WireGuard Peer avant de démarrer le tunnel.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt install resolvconf
</code></pre></div></div>
<p>Si le client utilise systemd-network pour la configuration réseau, il faut vérifier que le fichier /etc/resolv.conf existe et contient un nameserver pour la DNS</p>
<p>Pour démarrer le tunnel</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo wg-quick up wg0
</code></pre></div></div>
<p>Si vous définissez les adresses IP autorisées client PEER sur 0.0.0.0/0 et ::/0 (ou si vous utilisez dautres plages que celles que vous avez choisies pour le VPN), votre résultat ressemblera à ce qui suit</p>
<p><img src="/images/wg_xoyaz05.png" alt="" /></p>
<p>Vous pouvez vérifier létat du tunnel sur le client peer à laide de la commande wg</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo wg
</code></pre></div></div>
<p><img src="/images/wg_xoyaz07.png" alt="" /></p>
<p>Vous pouvez également vérifier à nouveau létat du serveur et vous obtiendrez un résultat similaire.</p>
<p>Vérifiez que votre client peer utilise le VPN en utilisant les commandes <code class="language-plaintext highlighter-rouge">ip route</code> et <code class="language-plaintext highlighter-rouge">ip -6 route</code>. Si vous utilisez le VPN comme passerelle pour tout votre trafic Internet, vérifiez quelle interface sera utilisée pour le trafic destiné aux résolveurs DNS <code class="language-plaintext highlighter-rouge">1.1.1.1</code> et <code class="language-plaintext highlighter-rouge">2606:4700:4700::1111</code> de CloudFlare.</p>
<p>Si vous nutilisez WireGuard que pour accéder aux ressources du VPN, substituez une adresse IPv4 ou IPv6 valide comme la passerelle elle-même dans ces commandes. Par exemple 10.22.1.1 ou fd0d:86fa:c3bc::1.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip route get 1.1.1.1
</code></pre></div></div>
<p><img src="/images/wg_xoyaz08.png" alt="" /></p>
<p>Remarquez que le périphérique wg0 est utilisé et que ladresse IPv4 10.22.1.2 que vous avez attribuée au client PEER est utilisée. De même, si vous utilisez IPv6, exécutez ce qui suit :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip -6 route get 2606:4700:4700::1111
</code></pre></div></div>
<p><img src="/images/wg_xoyaz09.png" alt="" /></p>
<p>Notez à nouveau linterface wg0 et ladresse IPv6 fddb:a8d3:7f3a::2 que vous avez attribuée au client PEER</p>
<p>Si votre client PEER dispose dun navigateur, vous pouvez également visiter ipleak.net et ipv6-test.com pour confirmer que votre homologue achemine son trafic via le VPN.</p>
<p>Pour se déconnecter</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo wg-quick down wg0
</code></pre></div></div>
<p><img src="/images/wg_xoyaz10.png" alt="" /></p>
<p>Lutilisation dun service systemd signifie que vous pouvez configurer WireGuard pour quil démarre au démarrage afin que vous puissiez vous connecter à votre VPN à tout moment tant que le serveur est en cours dexécution. Pour ce faire, activer et démarrer le service <code class="language-plaintext highlighter-rouge">wg-quick</code> pour le tunnel <code class="language-plaintext highlighter-rouge">wg0</code> que vous avez défini en lajoutant à systemctl</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl enable wg-quick@wg0.service --now
</code></pre></div></div>
<h3 id="test-avec-ipleaknet">Test avec ipleak.net</h3>
<p>Test IPV4</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl https://ipv4.ipleak.net/json/
</code></pre></div></div>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"as_number"</span><span class="p">:</span><span class="w"> </span><span class="mi">212531</span><span class="p">,</span><span class="w">
</span><span class="nl">"isp_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"UAB Interneto vizija"</span><span class="p">,</span><span class="w">
</span><span class="nl">"country_code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"LT"</span><span class="p">,</span><span class="w">
</span><span class="nl">"country_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Lithuania"</span><span class="p">,</span><span class="w">
</span><span class="nl">"region_code"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
</span><span class="nl">"region_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
</span><span class="nl">"continent_code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"EU"</span><span class="p">,</span><span class="w">
</span><span class="nl">"continent_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Europe"</span><span class="p">,</span><span class="w">
</span><span class="nl">"city_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
</span><span class="nl">"postal_code"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
</span><span class="nl">"postal_confidence"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
</span><span class="nl">"latitude"</span><span class="p">:</span><span class="w"> </span><span class="mf">55.4167</span><span class="p">,</span><span class="w">
</span><span class="nl">"longitude"</span><span class="p">:</span><span class="w"> </span><span class="mi">24</span><span class="p">,</span><span class="w">
</span><span class="nl">"accuracy_radius"</span><span class="p">:</span><span class="w"> </span><span class="mi">200</span><span class="p">,</span><span class="w">
</span><span class="nl">"time_zone"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Europe</span><span class="se">\/</span><span class="s2">Vilnius"</span><span class="p">,</span><span class="w">
</span><span class="nl">"metro_code"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
</span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"min"</span><span class="p">,</span><span class="w">
</span><span class="nl">"cache"</span><span class="p">:</span><span class="w"> </span><span class="mi">1699970444</span><span class="p">,</span><span class="w">
</span><span class="nl">"ip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"195.181.242.156"</span><span class="p">,</span><span class="w">
</span><span class="nl">"reverse"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
</span><span class="nl">"query_text"</span><span class="p">:</span><span class="w"> </span><span class="s2">"195.181.242.156"</span><span class="p">,</span><span class="w">
</span><span class="nl">"query_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"myip"</span><span class="p">,</span><span class="w">
</span><span class="nl">"query_date"</span><span class="p">:</span><span class="w"> </span><span class="mi">1699970444</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>Test IPV6</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl https://ipv6.ipleak.net/json/
</code></pre></div></div>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"as_number"</span><span class="p">:</span><span class="w"> </span><span class="mi">62282</span><span class="p">,</span><span class="w">
</span><span class="nl">"isp_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"UAB Rakrejus"</span><span class="p">,</span><span class="w">
</span><span class="nl">"country_code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"LT"</span><span class="p">,</span><span class="w">
</span><span class="nl">"country_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Lithuania"</span><span class="p">,</span><span class="w">
</span><span class="nl">"region_code"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
</span><span class="nl">"region_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
</span><span class="nl">"continent_code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"EU"</span><span class="p">,</span><span class="w">
</span><span class="nl">"continent_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Europe"</span><span class="p">,</span><span class="w">
</span><span class="nl">"city_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
</span><span class="nl">"postal_code"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
</span><span class="nl">"postal_confidence"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
</span><span class="nl">"latitude"</span><span class="p">:</span><span class="w"> </span><span class="mf">55.4167</span><span class="p">,</span><span class="w">
</span><span class="nl">"longitude"</span><span class="p">:</span><span class="w"> </span><span class="mi">24</span><span class="p">,</span><span class="w">
</span><span class="nl">"accuracy_radius"</span><span class="p">:</span><span class="w"> </span><span class="mi">200</span><span class="p">,</span><span class="w">
</span><span class="nl">"time_zone"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Europe</span><span class="se">\/</span><span class="s2">Vilnius"</span><span class="p">,</span><span class="w">
</span><span class="nl">"metro_code"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
</span><span class="nl">"level"</span><span class="p">:</span><span class="w"> </span><span class="s2">"min"</span><span class="p">,</span><span class="w">
</span><span class="nl">"cache"</span><span class="p">:</span><span class="w"> </span><span class="mi">1699970480</span><span class="p">,</span><span class="w">
</span><span class="nl">"ip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2a02:7b40:c3b5:f29c::1"</span><span class="p">,</span><span class="w">
</span><span class="nl">"reverse"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
</span><span class="nl">"query_text"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2a02:7b40:c3b5:f29c::1"</span><span class="p">,</span><span class="w">
</span><span class="nl">"query_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"myip"</span><span class="p">,</span><span class="w">
</span><span class="nl">"query_date"</span><span class="p">:</span><span class="w"> </span><span class="mi">1699970480</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<h3 id="générer-un-code-qr-pour-les-clients">Générer un code QR pour les clients</h3>
<p>Prérequis, installer qrencode : <code class="language-plaintext highlighter-rouge">sudo apt install qrencode</code></p>
<p>Les fichiers de configuration peer_A.conf, peer_B.conf et peer_C.conf des clients “peer” sont dans le dossier <code class="language-plaintext highlighter-rouge">/etc/wireguard/</code> du serveur</p>
<p>Si le client est un appareil mobile tel quun téléphone, qrencode peut être utilisé pour générer le code QR de configuration du client et lafficher dans le terminal</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qrencode -t ansiutf8 -r /etc/wireguard/peer_B.conf
</code></pre></div></div>
<p>Sur un mobile android , utiliser lapplication <strong>WireGuard</strong> pour le scan du QrCode</p>
<h2 id="wireguard-web">Wireguard web</h2>
<p><em>Mise en place dune gestion des clients wireguard</em></p>
<p><a href="https://gitea.xoyize.xyz/yako/wg-webui-fr">WireGuard UI pour la gestion des configurations clients et serveur</a></p>
<p>Le fichier</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /opt/appwg/.env
SERVER=127.0.0.1
PORT=8080
GIN_MODE=debug
WG_CONF_DIR=/etc/wireguard
WG_INTERFACE_NAME=wg0.conf
SMTP_HOST=127.0.0.1
SMTP_PORT=25
SMTP_USERNAME=""
SMTP_PASSWORD=""
SMTP_FROM="wg-web-ui &lt;ian@xoyaz.xyz&gt;"
#/etc/systemd/system/wgweb.service
[Unit]
Description=Wireguard web
After=network.target
[Service]
Type=simple
Restart=on-failure
RestartSec=10
WorkingDirectory=/opt/appwg
ExecStart=/opt/appwg/wg-ui
[Install]
WantedBy=multi-user.target
</code></pre></div></div>
<h2 id="serveur-messagerie">Serveur messagerie</h2>
<p><a href="/2023/12/24/Serveur_messagerie_IMAP_SMTP.html">Serveur de messagerie IMAP SMTP</a></p>
</div>
<div class="d-print-none"><footer class="article__footer"><meta itemprop="dateModified" content="2023-11-14T00: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="/2023/11/03/Archlinux-PACMAN_Liste_des_paquets_installes_et_reinstallation.html">PACMAN Créer une liste des paquets installés et les installer plus tard dans Arch Linux</a></div><div class="next"><span>SUIVANT</span><a href="/2023/11/19/Qemu-KVM-Machine_virtuelle_debian_12_image_cloud_Qcow2.html">Lenovo KVM - Machine virtuelle debian 12 (vm-debian12)</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>