yannstatic/static/2018/08/10/ProtonVPN-Connexions.html

2229 lines
205 KiB
HTML
Raw Permalink 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>Connexions VPN fournisseur ProtonVPN - YannStatic</title>
<meta name="description" content="">
<link rel="canonical" href="https://static.rnmkcy.eu/2018/08/10/ProtonVPN-Connexions.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">
<ul><li class="navigation__item"><a href="/archive.html">Etiquettes</a></li><li class="navigation__item"><a href="/htmldoc.html">Documents</a></li><li class="navigation__item"><a href="/liens_ttrss.html">Liens</a></li><li class="navigation__item"><a href="/aide-jekyll-text-theme.html">Aide</a></li></ul>
</nav></div>
</header>
</div><div class="page__content"><div class ="main"><div class="grid grid--reverse">
<div class="col-main cell cell--auto"><!-- start custom main top snippet --><div id="results-container" class="search-result js-search-result"></div><!-- end custom main top snippet -->
<article itemscope itemtype="http://schema.org/Article"><div class="article__header"><header><h1 style="color:Tomato;">Connexions VPN fournisseur ProtonVPN</h1></header></div><meta itemprop="headline" content="Connexions VPN fournisseur ProtonVPN"><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=vpn">vpn</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">10&nbsp;août&nbsp;&nbsp;2018</span></li></ul></div><meta itemprop="datePublished" content="2018-08-10T00:00:00+02:00">
<meta itemprop="keywords" content="vpn"><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><a href="https://protonvpn.com/"><img src="/images/protonvpn-logo-green.png" alt="ProtonVPN" /></a></p>
<p>ProtonVPN peut sutiliser sur toutes les plateformes avec le client officiel OpenVPN<br />
Configuration</p>
<ul>
<li>AES-256-CBC pour la data channel</li>
<li>ECDHE-RSA-AES256-GCM-SHA384 pour la control channel</li>
<li>RSA 2048 bits</li>
<li>tls-auth</li>
<li>SHA-512 pour lauthentification HMAC</li>
<li>TLS 1.2</li>
</ul>
<p><em>Il faut ouvrir un compte <a href="https://protonvpn.com/">ProtonVPN </a> et régler un abonnement à lannée pour disposer des éléments de paramétrage OpenVPN</em></p>
<p>On va utiliser une connexion <strong>Secure Core VPN</strong></p>
<p><strong><u>Qu'est-ce que Secure Core VPN?</u></strong><br />
ProtonVPN dispose dun mode Secure Core pour améliorer la confidentialité des utilisateurs et la sécurité des données en atténuant certains des risques dun serveur VPN compromis.<br />
Une méthode commune pour exposer le trafic VPN consiste à compromettre le serveur qui gère votre trafic. Ce risque est particulièrement grave lorsque le serveur est situé dans une juridiction à haut risque. Pour atténuer ce risque, ProtonVPN emploie une technique appelée Secure Core. Cet article fournit une vue densemble des attaques que Secure Core atténue, de la manière dont il atteint une plus grande confidentialité VPN, ainsi que des instructions sur lactivation de Secure Core dans ProtonVPN.</p>
<p><strong><u>A. À quoi protège Secure Core?</u></strong><br />
Larchitecture Secure Core unique de ProtonVPN nous permet de nous défendre contre les attaques réseau auxquelles les autres VPN ne peuvent se défendre. Une configuration VPN classique implique un client qui transmet le trafic via un serveur VPN en route vers la destination finale. Cela signifie quun attaquant qui a le contrôle du serveur VPN ou la capacité de surveiller le réseau du serveur VPN sera capable de faire correspondre les clients VPN avec leur destination, annulant les avantages de la vie privée du VPN.<br />
De telles attaques de synchronisation / corrélation ne sont pas difficiles à accomplir. Dans les pays ayant des règlements Internet restrictifs (Chine, Russie, Iran, Turquie, etc.), ou des pays dotés de larges pouvoirs de surveillance (États-Unis, Royaume-Uni, etc.), les agences de surveillance de lEtat ont généralement le pouvoir de contraindre soit le fournisseur VPN, Fournisseur de serveurs du fournisseur VPN, pour aider à la surveillance du réseau. Par conséquent, même si ProtonVPN est basé en Suisse, nous ne pouvons pas savoir avec certitude que nos serveurs VPN dans dautres pays tels que les États-Unis ou le Royaume-Uni ne sont pas surveillés et que la confidentialité des utilisateurs est compromise.</p>
<p><strong><u>B. Comment Secure Core augmente-t-il la confidentialité VPN?</u></strong><br />
Secure Core nous permet de nous défendre contre cette menace pour la confidentialité VPN en transmettant le trafic utilisateur via plusieurs serveurs. Lorsque vous vous connectez à un serveur dans une juridiction à risque élevé comme les États-Unis, votre trafic passera dabord par nos serveurs Secure Core. Par conséquent, même si notre serveur VPN américain est surveillé, un attaquant ne pourra suivre le trafic quau bord de notre réseau Secure Core, rendant ainsi beaucoup plus difficile la découverte de la véritable adresse IP et de lemplacement des utilisateurs ProtonVPN.</p>
<p>Nous avons également pris des mesures extraordinaires pour défendre nos serveurs Secure Core. Tout dabord, les serveurs sont situés dans des pays spécifiquement sélectionnés avec des lois de confidentialité très fortes (Islande, Suisse et Suède). Les serveurs Secure Core sont également situés dans des datacenters de haute sécurité pour assurer une sécurité physique forte. Linfrastructure ProtonVPN en Suisse et en Suède est située dans les centres de données souterrains, tandis que nos serveurs islandais sont situés dans une ancienne base militaire. En outre, les serveurs Secure Core sont entièrement détenus par nous et sont également fournis par nous (expédiés sur place directement depuis nos bureaux). Enfin, les serveurs Secure Core sont connectés à Internet en utilisant notre propre réseau dédié avec des adresses IP qui sont détenues et exploitées par notre propre registre Local Internet (LIR).</p>
<p><strong>Ces mesures nous offrent un niveau de certitude beaucoup plus élevé que nos serveurs et notre réseau Secure Core ne sont pas manipulés. Bien quil ny ait pas de sécurité à 100%, Secure Core est lune des nombreuses façons dont ProtonVPN offre une meilleure sécurité et confidentialité VPN en protégeant contre les attaques complexes auxquelles les autres VPN ne peuvent se défendre.</strong></p>
<h2 id="protonvpn-archlinux">ProtonVPN Archlinux</h2>
<p>Article original, <a href="https://rawsec.ml/en/archlinux-How-to-use-ProtonVPN/">How to use ProtonVPN on Arch Linux </a></p>
<h3 id="installer-openvpn">Installer OpenVPN</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo pacman -S openvpn --needed
</code></pre></div></div>
<p>Pour éviter les fuites DNS, vous aurez besoin dun paquet supplémentaire, pour plus dinformations voir <a href="https://wiki.archlinux.org/index.php/OpenVPN#DNS">Arch Linux Wiki</a>.</p>
<p>Suivant la version de <strong>systemd</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl --version
systemd 239
</code></pre></div></div>
<h4 id="version-systemd--229">version systemd &lt; 229</h4>
<p>Installer <strong>openvpn-update-resolv-conf</strong> ou <strong>openvpn-update-resolv-conf-git</strong>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yaourt -S openvpn-update-resolv-conf-git --needed
</code></pre></div></div>
<p>create: 2018-08-10
modification des fichiers de configuration ProtonVPN</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf
</code></pre></div></div>
<p>remplacer par</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setenv PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf
down-pre
</code></pre></div></div>
<p>ou bien si vous ne souhaitez pas modifier la configuration de votre client, vous pouvez ajouter les options suivantes à votre commande <strong>openvpn</strong>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>--setenv PATH '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' --up /etc/openvpn/update-resolv-conf --/etc/openvpn/update-resolv-conf --down-pre
</code></pre></div></div>
<h4 id="version-systemd--229-1">version systemd &gt;= 229</h4>
<p><em>Depuis systemd 229, systemd-networkd a exposé une API à travers DBus permettant la gestion de la configuration DNS sur une base par liaison. Des outils comme openresolv peuvent ne pas fonctionner de manière fiable lorsque /etc/resolv.conf est géré par systemd-resolved, et ne fonctionneront pas du tout si vous utilisez resolve au lieu de dns dans votre fichier /etc/nsswitch.conf.</em></p>
<blockquote>
<p>Le script <strong>update-systemd-resolved</strong> est une autre alternative et relie OpenVPN avec systemd-resolved via DBus pour mettre à jour les enregistrements DNS.</p>
</blockquote>
<p>Installer <strong>openvpn-update-systemd-resolved</strong>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yaourt -S openvpn-update-systemd-resolved --needed
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>To complete the installation, please add this script to your OpenVPN
settings for each of the VPNs you wish it to manage the DNS for:
script-security 2
setenv PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
up /etc/openvpn/scripts/update-systemd-resolved
down /etc/openvpn/scripts/update-systemd-resolved
down-pre
The path to the script has changed. Please ensure you update all your
configuration files to match this:
up /etc/openvpn/scripts/update-systemd-resolved
down /etc/openvpn/scripts/update-systemd-resolved
</code></pre></div></div>
<p>create: 2018-08-10
modification des fichiers de configuration ProtonVPN</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf
</code></pre></div></div>
<p>remplacer par</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>script-security 2
setenv PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
up /etc/openvpn/scripts/update-systemd-resolved
down /etc/openvpn/scripts/update-systemd-resolved
down-pre
</code></pre></div></div>
<p>ou bien si vous ne souhaitez pas modifier la configuration de votre client, vous pouvez ajouter les options suivantes à votre commande <strong>openvpn</strong>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>--setenv PATH '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' --down-pre
</code></pre></div></div>
<h3 id="obtenir-les-fichiers-de-configuration-de-protonvpn">Obtenir les fichiers de configuration de ProtonVPN</h3>
<p>Créez un compte ou connectez-vous sur <a href="https://account.protonvpn.com/">account. protonvpn. com</a>.<br />
Accédez au menu de téléchargement et téléchargez le fichier de configuration dont vous avez besoin (choisissez le pays et le protocole).</p>
<blockquote>
<p>Remarque: si vous utilisez un compte gratuit, seules les options Connexion au serveur et un nombre restreint de pays et de serveurs sont disponibles:</p>
</blockquote>
<p>Trouvez vos identifiants OpenVPN dans le menu Compte. Vous devez initialiser le mot de passe OpenVPN.</p>
<h3 id="connexion-via-linterface-de-ligne-de-commande-cli">Connexion via linterface de ligne de commande (CLI)</h3>
<p>Lancez openvpn avec des privilèges:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo openvpn nl-07.protonvpn.com.udp1194.ovpn
</code></pre></div></div>
<blockquote>
<p>Note: noubliez pas dutiliser les arguments supplémentaires que nous avons vus lors de linstallation si vous navez pas changé les fichiers de configuration.</p>
</blockquote>
<p>Par exemple, jutilise</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>openvpn --setenv PATH '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' --up /etc/openvpn/scripts/update-systemd-resolved --down /etc/openvpn/scripts/update-systemd-resolved --down-pre --config fr.protonvpn.com.udp1194.ovpn
</code></pre></div></div>
<p>veuillez noter que lors de lutilisation darguments vous devez utiliser <strong>config</strong> autrement vous aurez un message derreur comme<br />
<strong>Options error: Unrecognized option or missing or extra parameter(s) in [CMD-LINE]:1: /etc/openvpn/update-resolv-conf (2.4.2)</strong>.</p>
<p>Lorsque vous voyez la séquence dinitialisation terminée, vous êtes connecté avec succès.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[...]
Sun Jul 22 19:27:32 2018 TUN/TAP device tun0 opened
Sun Jul 22 19:27:32 2018 TUN/TAP TX queue length set to 100
Sun Jul 22 19:27:32 2018 do_ifconfig, tt-&gt;did_ifconfig_ipv6_setup=0
Sun Jul 22 19:27:32 2018 /usr/bin/ip link set dev tun0 up mtu 1500
Sun Jul 22 19:27:32 2018 /usr/bin/ip addr add dev tun0 10.8.8.27/24 broadcast 10.8.8.255
Sun Jul 22 19:27:32 2018 /etc/openvpn/scripts/update-systemd-resolved tun0 1500 1585 10.8.8.27 255.255.255.0 init
&lt;14&gt;Jul 22 19:27:32 update-systemd-resolved: Link 'tun0' coming up
&lt;14&gt;Jul 22 19:27:32 update-systemd-resolved: Adding IPv4 DNS Server 10.8.8.1
&lt;14&gt;Jul 22 19:27:32 update-systemd-resolved: SetLinkDNS(3 1 2 4 10 8 8 1)
Sun Jul 22 19:27:32 2018 /usr/bin/ip route add 185.94.189.188/32 via 192.168.0.254
Sun Jul 22 19:27:32 2018 /usr/bin/ip route add 0.0.0.0/1 via 10.8.8.1
Sun Jul 22 19:27:32 2018 /usr/bin/ip route add 128.0.0.0/1 via 10.8.8.1
Sun Jul 22 19:27:32 2018 Initialization Sequence Completed
</code></pre></div></div>
<blockquote>
<p>Il est possible de fournir lutilisateur et son mot de passe dans la ligne de commande <br />
Le fichier (<code class="language-plaintext highlighter-rouge">protonvpn_userpass.txt</code> par ex:) comporte 2 lignes : ligne 1 ,Utilisateur et ligne 2, Mot de passe.</p>
</blockquote>
<p>Dans la commande ci-dessous , le fichier de configuration se nomme <code class="language-plaintext highlighter-rouge">protonvpn.conf</code><br />
La ligne de commande <br />
<code class="language-plaintext highlighter-rouge">openvpn --setenv PATH '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' --up /etc/openvpn/scripts/update-systemd-resolved --down /etc/openvpn/scripts/update-systemd-resolved --down-pre --config /etc/openvpn/client/protonvpn.conf --auth-user-pass /etc/openvpn/client/protonvpn_userpass.txt</code></p>
<h2 id="protonvpn-auto-connexion">ProtonVPN auto connexion</h2>
<p>Pour utiliser le dispatcher, il faut créer des scripts dans le répertoire <strong>/etc/NetworkManager/dispatcher.d</strong>, le fichier peut avoir un préfixe pour donner un ordre de lancement, exemple: 10-netfs, 20-ntpd <br />
Le script en lui même prend 2 arguments, lun pour le <em>nom de linterface</em>, lautre pour le <em>statut</em>, voici un exemple:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/bin/sh
INTERFACE=$1 # Le nom de l'interface
STATUS=$2 # Le statut
case "$STATUS" in
'up') exec systemctl start [service];;
'down') exec systemctl stop [service];;
esac
</code></pre></div></div>
<p>Afin que les scripts sexécutent, il est nécessaire dactiver le dispatcher :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl enable NetworkManager-dispatcher
</code></pre></div></div>
<h3 id="networkmanager---connexion-auto-au-vpn">NetworkManager - Connexion auto au VPN</h3>
<p>Connexion automatique au VPN quand le réseau est établi ou la wifi sur les portables.<br />
Pour se garder la possibilité de se connecter sur le réseau sans VPN (ordinateur de bureau) , création dune seconde connexion réseau filaire en mode graphique nommée <strong>Free.fr</strong><br />
Créer la connexion VPN en mode graphique <strong>Connexions VPN &gt; Configure VPN</strong><br />
2 cas de figure pour une exécution automatique</p>
<ol>
<li><u>Ajout mot de passe à la configuration VPN</u></li>
<li><u>Créer un fichier mot de passe</u></li>
<li><u>Partie commune à 1 et 2</u></li>
</ol>
<p>1-<u>Ajout mot de passe à la configuration VPN</u><br />
Modification en ligne de commande du fichier de connexion vpn <strong>/etc/NetworkManager/system-connections/se-fr-protonvpn</strong> ,<em>password-flags</em> passe à 0 et ajout configuration <em>vpn-secrets</em></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[vpn]
....
password-flags=0
[vpn-secrets]
password=your_password
</code></pre></div></div>
<p>Sur un <u>ordinateur de bureau à liaison filaire (ethernet)</u>,on va créer un fichier <strong>/etc/NetworkManager/dispatcher.d/10-openvpn</strong> en mode root qui va activer ou désactiver la configuration VPN<br />
Si la connexion <strong>Free.fr</strong> est active, on lance le VPN</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/bin/bash
VPN_NAME="se-fr-01.protonvpn.com.udp"
case "$2" in
up)
if [ "$CONNECTION_ID" == "Free.fr" ]; then
nmcli con up id "$VPN_NAME"
fi
;;
down)
if nmcli con show --active | grep "$VPN_NAME"; then
nmcli con down id "$VPN_NAME"
fi
;;
esac
</code></pre></div></div>
<p>2-<u>Créer un fichier mot de passe</u> <br />
Créer le dossier qui va contenir le fichier</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo mkdir /etc/mdp
</code></pre></div></div>
<p>Le fichier mot de passe <strong>vpn</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/mdp/vpn
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vpn.secrets.password:'mot_de_passe_connexion_vpn'
</code></pre></div></div>
<p>Les droits</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo chmod 600 /etc/mdp/vpn
</code></pre></div></div>
<p>Sur un <u>ordinateur de bureau à liaison filaire (ethernet)</u>,on va créer un fichier <strong>/etc/NetworkManager/dispatcher.d/10-openvpn</strong> en mode root qui va activer ou désactiver la configuration VPN</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/bin/bash
VPN_NAME="se-fr-01.protonvpn.com.udp"
case "$2" in
up)
if [ "$CONNECTION_ID" == "Free.fr" ]; then
nmcli con up id "$VPN_NAME" passwd-file /etc/mdp/vpn
fi
;;
down)
if nmcli con show --active | grep "$VPN_NAME"; then
nmcli con down id "$VPN_NAME"
fi
;;
esac
</code></pre></div></div>
<p>3-<u>Partie commune à 1 et 2</u><br />
Changer les droits</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo chown root:root /etc/NetworkManager/dispatcher.d/10-openvpn
sudo chmod 755 /etc/NetworkManager/dispatcher.d/10-openvpn
</code></pre></div></div>
<p>Afin que les scripts sexécutent, il est nécessaire dactiver le dispatcher</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl enable NetworkManager-dispatcher
</code></pre></div></div>
<p>Démarrer le service</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl start NetworkManager-dispatcher.service
</code></pre></div></div>
<p>Sur un <u>ordinateur portable Wifi</u>, on veut se connecter automatiquement au VPN quelque soit le réseau Wifi utilisé.<br />
On va créer un fichier <strong>/etc/NetworkManager/dispatcher.d/10-wifivpn</strong> en mode root qui va activer ou désactiver la configuration VPN<br />
<em>iwgetid</em> nécessite linstallation du paquet <strong>wireless_tools</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yaourt -S wireless_tools
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/bin/sh
VPN_NAME="se-fr-protonvpn"
ESSID=$(iwgetid -r)
interface=$1
status=$2
case $status in
up|vpn-down)
if iwgetid | grep -qs ":\"$ESSID\""; then
nmcli con up id "$VPN_NAME"
fi
;;
down)
if iwgetid | grep -qs ":\"$ESSID\""; then
if nmcli con show --active | grep "$VPN_NAME"; then
nmcli con down id "$VPN_NAME"
fi
fi
;;
esac
</code></pre></div></div>
<p>Changer les droits</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo chown root:root /etc/NetworkManager/dispatcher.d/10-wifivpn
sudo chmod 755 /etc/NetworkManager/dispatcher.d/10-wifivpn
</code></pre></div></div>
<p>Afin que les scripts sexécutent, il est nécessaire dactiver le dispatcher</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl enable NetworkManager-dispatcher
</code></pre></div></div>
<p>Démarrer le service</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl start NetworkManager-dispatcher.service
</code></pre></div></div>
<h2 id="attention-aux-connexions-à-distance">Attention aux connexions à distance</h2>
<p><strong>Lancer OpenVPN à distance (ex : via SSH) sur un serveur comme un VPS ou un serveur dédié peut être dangereux.</strong><br />
Lutilisation dun VPN changera votre adresse IP et votre passerelle par défaut de sorte que votre connexion à distance sera interrompue et que vous ne pourrez plus vous connecter à votre serveur. Pour récupérer, vous aurez besoin dun autre mode de sauvetage du réseau ou dun redémarrage brutal.</p>
<blockquote>
<p>Afin de ne pas interrompre votre connexion à distance active, vous devrez configurer la route appropriée avant de lancer openvpn.</p>
</blockquote>
<p>Méthode (<a href="https://serverfault.com/questions/659955/allowing-ssh-on-a-server-with-an-active-openvpn-client#answer-793700">allowing-ssh-on-a-server-with-an-active-openvpn-client</a>):<br />
Il utilise iptables et ip (iproute2). Ci-dessous, il est supposé que linterface de passerelle par défaut avant le démarrage dOpenVPN est “eth0”. Lidée est de sassurer que lorsquune connexion à eth0 est établie, même si eth0 nest plus linterface de passerelle par défaut, les paquets de réponse pour la connexion retournent sur eth0.<br />
Vous pouvez utiliser le même numéro pour la marque de connexion, la marque de pare-feu et la table de routage. Jai utilisé des nombres distincts pour rendre les différences entre eux plus évidentes.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># set "connection" mark of connection from eth0 when first packet of connection arrives</span>
<span class="c"># Définir la marque "connexion" de connexion à partir de eth0 lorsque le premier paquet de connexion arrive.</span>
<span class="nb">sudo </span>iptables <span class="nt">-t</span> mangle <span class="nt">-A</span> PREROUTING <span class="nt">-i</span> eth0 <span class="nt">-m</span> conntrack <span class="nt">--ctstate</span> NEW <span class="nt">-j</span> CONNMARK <span class="nt">--set-mark</span> 1234
<span class="c"># set "firewall" mark for response packets in connection with our connection mark</span>
<span class="c"># Définir la marque "firewall" pour les paquets de réponse en relation avec notre marque de connexion.</span>
<span class="nb">sudo </span>iptables <span class="nt">-t</span> mangle <span class="nt">-A</span> OUTPUT <span class="nt">-m</span> connmark <span class="nt">--mark</span> 1234 <span class="nt">-j</span> MARK <span class="nt">--set-mark</span> 4321
<span class="c"># our routing table with eth0 as gateway interface</span>
<span class="c"># notre table de routage avec eth0 comme interface de passerelle</span>
<span class="nb">sudo </span>ip route add default dev eth0 table 3412
<span class="c"># route packets with our firewall mark using our routing table</span>
<span class="c"># de paquets de route avec notre marque de pare-feu en utilisant notre table de routage.</span>
<span class="nb">sudo </span>ip rule add fwmark 4321 table 3412
</code></pre></div></div>
<h2 id="erreurs">Erreurs</h2>
<p>Lancer en mode commande pour analyse</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>openvpn --setenv PATH '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' --up /etc/openvpn/scripts/update-systemd-resolved --down /etc/openvpn/scripts/update-systemd-resolved --down-pre --config se-fr-01.protonvpn.com.udp1194.ovpn
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[...]
Fri Aug 10 07:34:24 2018 TUN/TAP device tun0 opened
Fri Aug 10 07:34:24 2018 TUN/TAP TX queue length set to 100
Fri Aug 10 07:34:24 2018 do_ifconfig, tt-&gt;did_ifconfig_ipv6_setup=0
Fri Aug 10 07:34:24 2018 /usr/bin/ip link set dev tun0 up mtu 1500
Fri Aug 10 07:34:24 2018 /usr/bin/ip addr add dev tun0 10.8.8.16/24 broadcast 10.8.8.255
Fri Aug 10 07:34:24 2018 /etc/openvpn/scripts/update-systemd-resolved tun0 1500 1585 10.8.8.16 255.255.255.0 init
&lt;14&gt;Aug 10 07:34:24 update-systemd-resolved: Link 'tun0' coming up
&lt;14&gt;Aug 10 07:34:24 update-systemd-resolved: Adding IPv4 DNS Server 10.8.8.1
&lt;14&gt;Aug 10 07:34:24 update-systemd-resolved: SetLinkDNS(5 1 2 4 10 8 8 1)
Unit dbus-org.freedesktop.resolve1.service not found.
&lt;8&gt;Aug 10 07:34:24 update-systemd-resolved: 'busctl' exited with status 1
Fri Aug 10 07:34:24 2018 WARNING: Failed running command (--up/--down): external program exited with error status: 1
Fri Aug 10 07:34:24 2018 Exiting due to fatal error
</code></pre></div></div>
<p>Le service de résolution nest pas actif</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl status systemd-resolved.service
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>● systemd-resolved.service - Network Name Resolution
Loaded: loaded (/usr/lib/systemd/system/systemd-resolved.service; disabled; vendor preset: enabled)
Active: inactive (dead)
Docs: man:systemd-resolved.service(8)
https://www.freedesktop.org/wiki/Software/systemd/resolved
https://www.freedesktop.org/wiki/Software/systemd/writing-network-configuration-managers
https://www.freedesktop.org/wiki/Software/systemd/writing-resolver-clients
Lancer le service de résolution
systemctl start systemd-resolved.service
et vérifier
systemctl status systemd-resolved.service
</code></pre></div></div>
<p>● systemd-resolved.service - Network Name Resolution
Loaded: loaded (/usr/lib/systemd/system/systemd-resolved.service; disabled; vendor preset: enabled)
Active: active (running) since Fri 2018-08-10 07:45:37 CEST; 6s ago
Docs: man:systemd-resolved.service(8)
https://www.freedesktop.org/wiki/Software/systemd/resolved
https://www.freedesktop.org/wiki/Software/systemd/writing-network-configuration-managers
https://www.freedesktop.org/wiki/Software/systemd/writing-resolver-clients
Main PID: 1953 (systemd-resolve)
Status: “Processing requests…”
Tasks: 1 (limit: 4915)
Memory: 2.1M
CGroup: /system.slice/systemd-resolved.service
└─1953 /usr/lib/systemd/systemd-resolved</p>
<p>août 10 07:45:37 e6230 systemd[1]: Starting Network Name Resolution…
août 10 07:45:37 e6230 systemd-resolved[1953]: Positive Trust Anchors:
août 10 07:45:37 e6230 systemd-resolved[1953]: . IN DS 19036 8 2 49aac11d7b6f6446702e54a1607371607a1a41855200fd2ce1cdde32f24e8fb5
août 10 07:45:37 e6230 systemd-resolved[1953]: . IN DS 20326 8 2 e06d44b80b8f1d39a95c0b0d7c65d08458e880409bbc683457104237c7f8ec8d
août 10 07:45:37 e6230 systemd-resolved[1953]: Negative trust anchors: 10.in-addr.arpa 16.172.in-addr.arpa 17.172.in-addr.arpa 1&gt;
août 10 07:45:37 e6230 systemd-resolved[1953]: Using system hostname e6230.
août 10 07:45:37 e6230 systemd[1]: Started Network Name Resolution.
août 10 07:45:37 e6230 systemd-resolved[1953]: request_name_destroy_callback n_ref=1
```</p>
<p>Activer le service de résolution pour le prochain démarrage</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl enable systemd-resolved.service
</code></pre></div></div>
<h2 id="liens">Liens</h2>
<ul>
<li><a href="https://wiki.archlinux.fr/NetworkManager">NetworkManager</a></li>
<li><a href="http://blog.deadlypenguin.com/blog/2017/04/24/vpn-auto-connect-command-line/">VPN Setup and Auto Connect from Command-line</a></li>
<li><a href="https://wiki.archlinux.org/index.php/NetworkManager#Network_services_with_NetworkManager_dispatcher">Network services with NetworkManager dispatcher</a></li>
<li><a href="http://www.techytalk.info/start-script-on-network-manager-successful-connection/comment-page-1/">Start shell script on Network Manager successful connection</a></li>
</ul>
<h2 id="gtkvpn">gtkvpn</h2>
<p><em>Gestionnaire des connexion vpn mullvad</em><br />
<a href="https://gitea.yanspm.com/yannick/gtkvpn">https://gitea.yanspm.com/yannick/gtkvpn</a></p>
</div>
<div class="d-print-none"><footer class="article__footer"><meta itemprop="dateModified" content="2018-08-10T00:00:00+02:00"><!-- start custom article footer snippet -->
<!-- end custom article footer snippet -->
<!--
<div align="right"><a type="application/rss+xml" href="/feed.xml" title="S'abonner"><i class="fa fa-rss fa-2x"></i></a>
&emsp;</div>
-->
</footer>
<div class="article__section-navigator clearfix"><div class="previous"><span>PRÉCÉDENT</span><a href="/2018/08/10/Gestion-des-bases-Mysql-MariaDB-avec-Adminer.html">Gestion des bases Mysql/MariaDB avec Adminer</a></div><div class="next"><span>SUIVANT</span><a href="/2018/08/11/Migration-Yunohost-version3-debian-stretch.html">Migration Yunohost 3.0 (debian stretch)</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>