2717 lines
226 KiB
HTML
2717 lines
226 KiB
HTML
|
<!DOCTYPE html><html lang="fr">
|
|||
|
<head><meta charset="utf-8">
|
|||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"><title>VirtualBox sur serveur xoyize.xyz (srvxo, ex PC2) + Machine virtuelle yunohost ouestline.net - YannStatic</title>
|
|||
|
|
|||
|
<meta name="description" content="">
|
|||
|
<link rel="canonical" href="https://static.rnmkcy.eu/2019/12/28/VirtualBox(virtualisation-linux)-serveur-xoyize.xyz(ex_PC2)-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} {title}</a></li>'
|
|||
|
searchResultTemplate: '<li><a href="{url}">{date} {title}</a></li>'
|
|||
|
})
|
|||
|
</script>
|
|||
|
<!-- Fin déclaration champ de recherche --></div><nav class="navigation">
|
|||
|
<ul><li class="navigation__item"><a href="/archive.html">Etiquettes</a></li><li class="navigation__item"><a href="/htmldoc.html">Documents</a></li><li class="navigation__item"><a href="/liens_ttrss.html">Liens</a></li><li class="navigation__item"><a href="/aide-jekyll-text-theme.html">Aide</a></li></ul>
|
|||
|
</nav></div>
|
|||
|
</header>
|
|||
|
|
|||
|
</div><div class="page__content"><div class ="main"><div class="grid grid--reverse">
|
|||
|
<div class="col-main cell cell--auto"><!-- start custom main top snippet --><div id="results-container" class="search-result js-search-result"></div><!-- end custom main top snippet -->
|
|||
|
<article itemscope itemtype="http://schema.org/Article"><div class="article__header"><header><h1 style="color:Tomato;">VirtualBox sur serveur xoyize.xyz (srvxo, ex PC2) + Machine virtuelle yunohost ouestline.net</h1></header></div><meta itemprop="headline" content="VirtualBox sur serveur xoyize.xyz (srvxo, ex PC2) + Machine virtuelle yunohost ouestline.net"><div class="article__info clearfix"><ul class="left-col menu"><li>
|
|||
|
<a class="button button--secondary button--pill button--sm"
|
|||
|
href="/archive.html?tag=virtuel">virtuel</a>
|
|||
|
</li></ul><ul class="right-col menu"><li>
|
|||
|
<i class="far fa-calendar-alt"></i> <span title="Création" style="color:#FF00FF">28 déc. 2019</span>
|
|||
|
|
|||
|
<span title="Modification" style="color:#00FF7F">20 févr. 2020</span></li></ul></div><meta itemprop="datePublished" content="2020-02-20T00:00:00+01:00">
|
|||
|
<meta itemprop="keywords" content="virtuel"><div class="js-article-content">
|
|||
|
<div class="layout--article"><!-- start custom article top snippet -->
|
|||
|
<style>
|
|||
|
#myBtn {
|
|||
|
display: none;
|
|||
|
position: fixed;
|
|||
|
bottom: 10px;
|
|||
|
right: 10px;
|
|||
|
z-index: 99;
|
|||
|
font-size: 12px;
|
|||
|
font-weight: bold;
|
|||
|
border: none;
|
|||
|
outline: none;
|
|||
|
background-color: white;
|
|||
|
color: black;
|
|||
|
cursor: pointer;
|
|||
|
padding: 5px;
|
|||
|
border-radius: 4px;
|
|||
|
}
|
|||
|
|
|||
|
#myBtn:hover {
|
|||
|
background-color: #555;
|
|||
|
}
|
|||
|
</style>
|
|||
|
|
|||
|
<button onclick="topFunction()" id="myBtn" title="Haut de page">⇧</button>
|
|||
|
|
|||
|
<script>
|
|||
|
//Get the button
|
|||
|
var mybutton = document.getElementById("myBtn");
|
|||
|
|
|||
|
// When the user scrolls down 20px from the top of the document, show the button
|
|||
|
window.onscroll = function() {scrollFunction()};
|
|||
|
|
|||
|
function scrollFunction() {
|
|||
|
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
|
|||
|
mybutton.style.display = "block";
|
|||
|
} else {
|
|||
|
mybutton.style.display = "none";
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// When the user clicks on the button, scroll to the top of the document
|
|||
|
function topFunction() {
|
|||
|
document.body.scrollTop = 0;
|
|||
|
document.documentElement.scrollTop = 0;
|
|||
|
}
|
|||
|
</script>
|
|||
|
|
|||
|
|
|||
|
<!-- end custom article top snippet -->
|
|||
|
<div class="article__content" itemprop="articleBody"><details>
|
|||
|
<summary><b>Afficher/cacher Sommaire</b></summary>
|
|||
|
<!-- affichage sommaire -->
|
|||
|
<div class="toc-aside js-toc-root"></div>
|
|||
|
</details><p><img src="/images/virtualbox6-logo.png" alt="image" width="200px" /></p>
|
|||
|
|
|||
|
<h2 id="virtualbox-on-headless-server">VirtualBox on Headless Server</h2>
|
|||
|
|
|||
|
<p><em>Installer virtualBox sur un serveur sans carte graphique</em></p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><a href="https://www.ostechnix.com/install-oracle-virtualbox-ubuntu-16-04-headless-server/">How to Install Oracle VirtualBox On Ubuntu 18.04.2 LTS Headless Server</a></li>
|
|||
|
<li><a href="https://www.tecmint.com/install-virtualbox-on-debian-10/">How to Install VirtualBox 6 on Debian 10</a></li>
|
|||
|
<li><a href="https://vorkbaard.nl/how-to-set-up-a-virtualbox-server-in-debian-9-web-interface-autostart-backup/">How to set up a VirtualBox server in Debian 9: web interface, autostart, backup</a></li>
|
|||
|
<li><a href="https://www.howtoforge.com/managing-a-headless-virtualbox-installation-with-phpvirtualbox-on-nginx-ubuntu-12.04">Managing A Headless VirtualBox Installation With phpvirtualbox On nginx (Ubuntu 12.04)</a></li>
|
|||
|
<li><a href="https://vorkbaard.nl/how-to-set-up-a-virtualbox-server-in-debian-9-web-interface-autostart-backup/">How to set up a VirtualBox server in Debian 9: web interface, autostart, backup</a></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>VirtualBox 6.0 est la dernière version majeure publiée par l’équipe Oracle. Cette version est publiée avec diverses améliorations de performances par rapport aux versions majeures précédentes. Cet article vous aide à installer VirtualBox sur le système Debian 10 Buster Linux.</p>
|
|||
|
|
|||
|
<h3 id="prérequis">Prérequis</h3>
|
|||
|
|
|||
|
<p>Connectez-vous à votre système de bureau Debian 10 Buster Linux avec les privilèges sudo utilisateur. Mettez ensuite à jour les packages actuellement installés sur votre système. Pour ce faire, exécutez simplement les commandes suivantes.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt update
|
|||
|
sudo apt upgrade
|
|||
|
sudo apt install build-essential dkms unzip wget
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>Un redémarrage de la machine est nécessaire <code class="language-plaintext highlighter-rouge">sudo systemctl reboot</code></p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<h3 id="configuration-du-référentiel-apt">Configuration du référentiel Apt</h3>
|
|||
|
|
|||
|
<p>Maintenant, importez la clé publique Oracle sur votre système, qui a signé les paquets Debian. Vous pouvez ajouter ces clés à l’aide des commandes suivantes.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget --no-check-certificate -q https://www.virtualbox.org/download/oracle_vbox_2016.asc -O- | sudo apt-key add -
|
|||
|
wget --no-check-certificate -q https://www.virtualbox.org/download/oracle_vbox.asc -O- | sudo apt-key add -
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>L’option <code class="language-plaintext highlighter-rouge">--no-check-certificate</code> est obligatoire sinon erreur <strong>gpg: aucune donnée OpenPGP valable n’a été trouvée.</strong></p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p>Configurez ensuite le référentiel apt sur votre système Debian 10 Buster. Cette commande ajoutera une entrée au fichier /etc/apt/sources.list à la fin du fichier.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo add-apt-repository "deb http://download.virtualbox.org/virtualbox/debian buster contrib"
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="installer-virtualbox-sur-debian-10">Installer VirtualBox sur Debian 10</h3>
|
|||
|
|
|||
|
<p>Après avoir terminé les étapes ci-dessus, installons VirtualBox à l’aide des commandes suivantes. Si vous avez déjà installé une ancienne version de VirtualBox, la commande ci-dessous la mettra à jour automatiquement.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt update
|
|||
|
sudo apt install virtualbox-6.0
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajout d’utilisateurs au groupe VirtualBox</p>
|
|||
|
|
|||
|
<p>Nous devons créer et ajouter notre utilisateur système au groupe <strong>vboxusers</strong> . Vous pouvez soit créer un utilisateur distinct et l’affecter au groupe vboxusers, soit utiliser l’utilisateur existant.</p>
|
|||
|
|
|||
|
<p><u>Créer un utilisateur *vbox*</u> <br />
|
|||
|
Configurer un compte pour VirtualBox afin qu’il ne fonctionne pas en tant que root.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo useradd -d /home/vbox -m -g vboxusers -s /bin/bash vbox
|
|||
|
sudo passwd vbox
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><u>Utilisateur existant</u><br />
|
|||
|
Exécuter la commande suivante pour l’ajouter au groupe vboxusers.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo usermod -aG vboxusers $USER
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>Veuillez noter que si vous utilisez un utilisateur distinct pour virtualbox, vous devez vous déconnecter et vous connecter à cet utilisateur particulier et effectuer les autres étapes.</p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p>Maintenant, exécutez la commande suivante pour vérifier si les modules du noyau de virtualbox sont chargés ou non.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl status vboxdrv
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Comme vous pouvez le voir dans la capture d’écran ci-dessus, le module vboxdrv est chargé et fonctionne!</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>● vboxdrv.service - VirtualBox Linux kernel module
|
|||
|
Loaded: loaded (/usr/lib/virtualbox/vboxdrv.sh; enabled; vendor preset: enabled)
|
|||
|
Active: active (exited) since Fri 2019-12-20 08:19:49 CET; 3min 1s ago
|
|||
|
Tasks: 0 (limit: 4915)
|
|||
|
Memory: 0B
|
|||
|
CGroup: /system.slice/vboxdrv.service
|
|||
|
|
|||
|
déc. 20 08:19:49 xoyize.xyz systemd[1]: Starting VirtualBox Linux kernel module...
|
|||
|
déc. 20 08:19:49 xoyize.xyz vboxdrv.sh[13280]: vboxdrv.sh: Starting VirtualBox services.
|
|||
|
déc. 20 08:19:49 xoyize.xyz systemd[1]: Started VirtualBox Linux kernel module.
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="installer-le-pack-dextension-virtualbox">Installer le pack d’extension VirtualBox</h3>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><a href="https://kifarunix.com/install-virtualbox-extension-pack-on-virtualbox-6-0/">Install VirtualBox Extension Pack on VirtualBox 6.0</a></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Le pack d’extension VirtualBox fournit les fonctionnalités suivantes aux invités VirtualBox.</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Le périphérique USB 2.0 virtuel (EHCI)</li>
|
|||
|
<li>Prise en charge de VirtualBox Remote Desktop Protocol (VRDP)</li>
|
|||
|
<li>Passthrough webcam hôte</li>
|
|||
|
<li>ROM de démarrage Intel PXE</li>
|
|||
|
<li>Prise en charge expérimentale du passthrough PCI sur les hôtes Linux</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Vérifier la version</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vboxmanage --version
|
|||
|
# 6.0.14r133895
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Téléchargez ici le dernier pack d’extension pour VirtualBox 6.0.x.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget https://download.virtualbox.org/virtualbox/6.0.14/Oracle_VM_VirtualBox_Extension_Pack-6.0.14.vbox-extpack
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Installez le pack d’extension à l’aide de la commande:</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo VBoxManage extpack install Oracle_VM_VirtualBox_Extension_Pack-6.0.14.vbox-extpack
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Successfully installed “Oracle VM VirtualBox Extension Pack”.</p>
|
|||
|
|
|||
|
<p>Nous avons installé avec succès Oracle VirtualBox avec pack d’extension dans le serveur Ubuntu 18.04 LTS. Il est temps de déployer des machines virtuelles. Reportez-vous au <a href="http://www.virtualbox.org/manual/ch08.html">guide officiel de virtualbox</a> pour commencer à créer et gérer des machines virtuelles en ligne de commande.</p>
|
|||
|
|
|||
|
<h3 id="créerdémarrer-une-machine-virtuelle-en-ligne-de-commande">Créer/démarrer une machine virtuelle en ligne de commande</h3>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>L’utilisateur “vbox” est seul autorisé à gérer virtualbox , il faut précéder les commandes de <code class="language-plaintext highlighter-rouge">sudo -u vbox</code></p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><a href="/files/html/How to Manage Oracle VirtualBox Virtual Machines from Command Line.htm">How to Manage Oracle VirtualBox Virtual Machines from Command Line-Lien HS</a></li>
|
|||
|
<li><a href="https://docs.oracle.com/cd/E97728_01/E97727/html/vboxmanage.html">VBoxManage (User Manual for Release 6.0)</a></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>On se connecte sur le serveur qui héberge VirtualBox via SSH</p>
|
|||
|
|
|||
|
<p>Liste des machines virtuelles sur le serveur</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -u vbox VBoxManage list vms
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>“debian9” {bbe16841-97ac-4ff8-85e3-d6c9dc0ce470}</p>
|
|||
|
|
|||
|
<p>Si l’extension d’affichage à distance VirtualBox n’a pas été activée, la procédure peut être répétée</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -u vbox VBoxManage modifyvm "debian9" --vrde on
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Il y a une machine virtuelle nommée “debian9”.<br />
|
|||
|
Le mode Headless peut être activé par la suite de deux manières :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -u vbox VBoxHeadless -s debian9
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Oracle VM VirtualBox Headless Interface 4.1.8
|
|||
|
(C) 2008-2019 Oracle Corporation
|
|||
|
Tous droits réservés.
|
|||
|
|
|||
|
Le serveur VRDE est à l'écoute sur le port 3389.
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>ou :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>VBoxManage startvm ubuntu-server --type headless
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Attendre que la VM "ubuntu-server" s'allume...
|
|||
|
La VM "ubuntu-server" a été démarrée avec succès.
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>La machine virtuelle est maintenant en cours d’exécution sans affichage<br />
|
|||
|
A la place, on se connecte à la machine par RDP ou SSH (en supposant que les configurations réseau et SSH correspondantes existent).</p>
|
|||
|
|
|||
|
<h2 id="phpvirtualbox-virtualbox-web-vmxoyizexyz">phpVirtualBox (VirtualBox web vm.xoyize.xyz)</h2>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Créer et gérer des machines virtuelles graphiquement avec <a href="https://github.com/phpvirtualbox/phpvirtualbox">phpVirtualBox</a></li>
|
|||
|
<li><a href="https://github.com/phpvirtualbox/phpvirtualbox/wiki/vboxweb-service-Configuration-in-Linux">vboxweb service Configuration in Linux</a></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p><em>PhpVirtualBox est un frontal gratuit basé sur le Web pour Oracle VirtualBox. Il est écrit en langage PHP. En utilisant phpVirtualBox, nous pouvons facilement créer, supprimer, gérer et administrer des machines virtuelles via un navigateur Web à partir de n’importe quel système distant sur le réseau.</em></p>
|
|||
|
|
|||
|
<p>Nous utilisons un logiciel libre de serveur Web (ou HTTP) <strong>nginx</strong></p>
|
|||
|
|
|||
|
<h3 id="installer">Installer</h3>
|
|||
|
|
|||
|
<p>Installer phpVirtualBox</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/phpvirtualbox/phpvirtualbox.git
|
|||
|
sudo mv phpvirtualbox /var/www/phpvirtualbox
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Configurer phpVirtualBox.</p>
|
|||
|
|
|||
|
<p>Copiez l’exemple de fichier de configuration comme indiqué ci-dessous.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo cp /var/www/phpvirtualbox/config.php-example /var/www/phpvirtualbox/config.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Editez le fichier config.php de phpVirtualBox:</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /var/www/phpvirtualbox/config.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Recherchez les lignes suivantes et remplacez le nom d’utilisateur et le mot de passe</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/* Username / Password for system user that runs VirtualBox */
|
|||
|
var $username = 'vbox';
|
|||
|
var $password = 'pass';
|
|||
|
|
|||
|
/* Default language. See languages folder for more language options.
|
|||
|
* Can also be changed in File -> Preferences -> Language in
|
|||
|
* phpVirtualBox.
|
|||
|
*/
|
|||
|
var $language = 'fr';
|
|||
|
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>Remplacer ‘pass’ par le mot de passe</p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p>Créer un nouveau fichier appelé <strong>/etc/default/virtualbox</strong> :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/default/virtualbox
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajoutez la ligne suivante.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>VBOXWEB_USER = vbox
|
|||
|
VBOXWEB_HOST=127.0.0.1
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="vhost-vmxoyizexyz-authentification-par-certificat-client">Vhost vm.xoyize.xyz (authentification par certificat client)</h3>
|
|||
|
|
|||
|
<p>Créer le vhost nginx</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/nginx/conf.d/vm.xoyize.xyz.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /etc/nginx/conf.d/vm.xoyize.xyz.conf
|
|||
|
##
|
|||
|
# Virtual Host vm.xoyize.xyz (phpVirtualbox)
|
|||
|
##
|
|||
|
|
|||
|
server {
|
|||
|
listen 80;
|
|||
|
listen [::]:80;
|
|||
|
|
|||
|
## redirect http to https ##
|
|||
|
server_name vm.xoyize.xyz;
|
|||
|
return 301 https://$server_name$request_uri;
|
|||
|
}
|
|||
|
|
|||
|
server {
|
|||
|
listen 443 ssl http2;
|
|||
|
listen [::]:443 ssl http2;
|
|||
|
server_name vm.xoyize.xyz;
|
|||
|
|
|||
|
include ssl_dh_headers_ocsp;
|
|||
|
# Authentification par certificat client
|
|||
|
include auth_certificat_client;
|
|||
|
|
|||
|
root /var/www/phpvirtualbox;
|
|||
|
index index.html index.php;
|
|||
|
|
|||
|
location / {
|
|||
|
try_files $uri $uri/ /index.php?$args;
|
|||
|
}
|
|||
|
|
|||
|
location ~ \.php$ {
|
|||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
|||
|
fastcgi_pass unix:/run/php/php7.3-fpm.sock; # PHP7.3
|
|||
|
fastcgi_index index.php;
|
|||
|
include fastcgi_params;
|
|||
|
fastcgi_param SCRIPT_FILENAME $request_filename;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
access_log /var/log/nginx/phpvirtualbox-access.log;
|
|||
|
error_log /var/log/nginx/phpvirtualbox-error.log;
|
|||
|
}
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Il faut installer le module PHP SOAP extension</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt install php7.3-soap
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Redémarrer les services PHP et nginx</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl restart php7.3-fpm
|
|||
|
sudo systemctl restart nginx
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Enfin, redémarrez votre système ou redémarrez simplement les services suivants pour terminer la configuration.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl restart vboxweb-service
|
|||
|
sudo systemctl restart vboxdrv
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Accéder à la console Web phpVirtualBox https://vm.xoyize.xyz</p>
|
|||
|
|
|||
|
<p><img src="/images/vm001.png" alt="" width="600" /><br />
|
|||
|
Entrez les informations d’identification de l’utilisateur administratif phpVirtualBox.<br />
|
|||
|
Le nom d’utilisateur par défaut et phpVirtualBox est admin / admin .</p>
|
|||
|
|
|||
|
<h3 id="récupération-du-mot-de-passe">Récupération du mot de passe</h3>
|
|||
|
|
|||
|
<p>Vous pouvez réinitialiser le mot de passe admin en renommant le fichier <strong>recovery.php-disabled</strong> dans le dossier de phpVirtualBox en <strong>recovery.php</strong> et en y accédant dans votre navigateur Web. Par exemple <em>http://HOST-OR-IP/phpvirtualbox/recovery.php</em></p>
|
|||
|
|
|||
|
<p>Cette page vous présente un formulaire simple. Cliquez sur le bouton Récupérer pour réinitialiser le mot de passe de l’utilisateur <em>admin</em> à la valeur par défaut de <em>admin</em>. Si vous avez supprimé le compte administrateur, il sera recréé.</p>
|
|||
|
|
|||
|
<p>Une fois cela fait, renommez <strong>recovery.php</strong> en <strong>recovery.php-disabled</strong>. phpVirtualBox refusera de fonctionner tant que recovery.php existera. Vous pouvez alors vous connecter avec les identifiants par défaut de admin / admin.</p>
|
|||
|
|
|||
|
<h3 id="désactivation-de-lauthentification-par-mot-de-passe">Désactivation de l’authentification par mot de passe</h3>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>Désactivation de l’authentification par mot de passe car on utilise une authentification par <strong>certificat client</strong></p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p>Si vous voulez désactiver l’authentification dans <em>phpVirtualBox</em>, ajoutez la ligne suivante dans le fichier <strong>config.php</strong> :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var $noAuth = true ;
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Une fois que cela est fait, aucun nom d’utilisateur / mot de passe ne sera nécessaire pour accéder à phpVirtualBox. De plus, les sections de phpVirtualBox relatives à l’authentification (changement de mot de passe, utilisateurs, etc.) ne seront pas visibles.</p>
|
|||
|
|
|||
|
<h3 id="créerdémarrer-une-machine-virtuelle-via-phpvirtualbox">Créer/démarrer une machine virtuelle via phpvirtualbox</h3>
|
|||
|
|
|||
|
<p>Après avoir créé une machine , activé “le bureau à distance” port 3389 puis démarré</p>
|
|||
|
|
|||
|
<p><img src="/images/phpvbox001.png" alt="" width="500" /></p>
|
|||
|
|
|||
|
<h3 id="accès-aux-machines-virtuelles-en-mode-console-rdp">Accès aux machines virtuelles en mode console (RDP)</h3>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><a href="http://www.virtualbox.org/manual/ch07.html#idp8971072">Remote virtual machines: Remote display (VRDP support)</a> (Oracle VM VirtualBox User Manual, Chapter 7)</li>
|
|||
|
<li>Pour pouvoir utiliser VRPDP, le <a href="#installer-le-pack-dextension-virtualbox">package d’extension pour VirtualBox</a> doit avoir été installé.</li>
|
|||
|
<li>Installer un client RDP : <code class="language-plaintext highlighter-rouge">sudo pacman -S rdesktop</code></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>L’affichage d’une machine virtuelle démarrée en mode sans affichage (headless) peut être réalisé via VRDP.</p>
|
|||
|
|
|||
|
<p>La connexion à une machine virtuelle via RDP ne fonctionnera que si VRDE a été activé pour la machine virtuelle. Après cela, n’importe quel client RDP peut être utilisé pour se connecter à l’hôte local ou utiliser l’adresse IP de l’hôte sur la machine virtuelle <br />
|
|||
|
Le serveur VRDE est à l’écoute sur le port 3389.</p>
|
|||
|
|
|||
|
<p>VirtualBox est hébergé sur un serveur (xoyize.xyz) avec un pare-feu , la seule entrée possible est le port SSH .<br />
|
|||
|
On va utiliser le port SSH (55035) du serveur pour rediriger tout le traffic du port 3389 de la machine virtuelle vers un port local (15000) de la machine appelante.</p>
|
|||
|
|
|||
|
<p>Exécuter la commande suivante sur un premier terminal</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh -L 15000:localhost:3389 admbust@xoyize.xyz -p 55035 -i /home/yannick/.ssh/vbox-srvbust-ed25519
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Exécuter la commande suivante sur un second terminal (<a href="https://linux.die.net/man/1/rdesktop">man rdesktop</a>)</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rdesktop -g 100% -P -z -k fr localhost:15000 # Automatic scaling of geometry
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Et vous obtenez une fenêtre console de la machine virtuelle</p>
|
|||
|
|
|||
|
<p><img src="/images/phpvbox002.png" alt="" width="200" /></p>
|
|||
|
|
|||
|
<h2 id="service-pour-startstop-machine-virtuelle">Service pour start/stop machine virtuelle</h2>
|
|||
|
|
|||
|
<p><em>Créer un service ‘systemd’ qui lancera automatiquement un serveur virtuel au démarrage</em></p>
|
|||
|
|
|||
|
<p>Le serveur à lancer est identifié par son uuid<br />
|
|||
|
Exemple pour un serveur avec des “instantanés” (snapshots) <br />
|
|||
|
Liste des machines</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -u vbox VBoxManage list vms # liste des machines virtuelles
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"ynh" {beec9cd5-8afb-4906-861d-f4c18f1545e3}
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>On veut démarrer automatiquement ‘ynh’</p>
|
|||
|
|
|||
|
<p>Création d’un service <strong>vmdebian.service</strong> sous systemd</p>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p><strong>ATTENTION!!!</strong><br />
|
|||
|
Utilisation de systemd pour démarrer et arrêter la coexistence de <strong>vm</strong> et <strong>phpvirtualbox</strong> <br />
|
|||
|
Si vous utilisez un script systemd pour démarrer et arrêter <strong>vm</strong>, dans votre <strong>vmdebian.service</strong> vous devez utiliser<br />
|
|||
|
<code class="language-plaintext highlighter-rouge">sudo -u vboxuser /usr/bin/VBoxHeadless ...</code><br />
|
|||
|
et NE PAS SPECIFIER utilisateur et groupe comme suit<br />
|
|||
|
[Service]
|
|||
|
User=vboxuser<br />
|
|||
|
Group=vboxuser<br />
|
|||
|
Ajoutez également <code class="language-plaintext highlighter-rouge">httpd.service</code> au paramètre <code class="language-plaintext highlighter-rouge">After</code> dans <strong>vmdebian.service</strong>.<br />
|
|||
|
Vous devez également démarrer et arrêter vm UNIQUEMENT à partir de systemd, sinon vous risquez de recevoir une erreur de connexion</p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p>Le fichier <strong>vmdebian.service</strong> sous systemd</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/systemd/system/vmdebian.service
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Contenu du fichier</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Unit]
|
|||
|
Description=Vbox Yunohost Service
|
|||
|
After=network.target vboxdrv.service httpd.service
|
|||
|
|
|||
|
[Service]
|
|||
|
Type=simple
|
|||
|
ExecStart=sudo -u vbox /usr/bin/vboxheadless -s 'ynh'
|
|||
|
ExecStop=sudo -u vbox /usr/bin/vboxmanage controlvm 'ynh' poweroff
|
|||
|
|
|||
|
|
|||
|
[Install]
|
|||
|
WantedBy=multi-user.target
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Lancer le service vmdebian après avoir créé ynh et installé yunohost:</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl daemon-reload
|
|||
|
sudo systemctl start vmdebian
|
|||
|
#Vérifier:
|
|||
|
sudo systemctl status vmdebian
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>● vmdebian.service - Vbox Yunohost Service
|
|||
|
Loaded: loaded (/etc/systemd/system/vmdebian.service; enabled; vendor preset: enabled)
|
|||
|
Active: active (running) since Thu 2020-02-20 11:44:26 CET; 12s ago
|
|||
|
Main PID: 24618 (sudo)
|
|||
|
Tasks: 27 (limit: 4915)
|
|||
|
Memory: 33.4M
|
|||
|
CGroup: /system.slice/vmdebian.service
|
|||
|
├─24618 /usr/bin/sudo -u vbox /usr/bin/vboxheadless -s ynh
|
|||
|
└─24619 /usr/lib/virtualbox/VBoxHeadless -s ynh
|
|||
|
|
|||
|
févr. 20 11:44:26 xoyize.xyz systemd[1]: Started Vbox Yunohost Service.
|
|||
|
févr. 20 11:44:26 xoyize.xyz sudo[24618]: root : TTY=unknown ; PWD=/ ; USER=vbox ; COMMAND=/usr/bin/vboxheadless -s ynh
|
|||
|
févr. 20 11:44:26 xoyize.xyz sudo[24618]: pam_unix(sudo:session): session opened for user vbox by (uid=0)
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Activer le service pour le redémarrage</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl enable vmdebian
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h1 id="machine-virtuelle-virtualbox---ynh-ouestlinenet">Machine virtuelle Virtualbox - ynh (ouestline.net)</h1>
|
|||
|
|
|||
|
<p><em>VirtualBox est installé sur le serveur xoyize.xyz , à l’adresse 192.168.0.45 dans le réseau local. On va utliser <strong><a href="https://vm.xoyize.xyz/">phpvirtualbox</a></strong> pour créer/gérer les machines virtuelles.</em></p>
|
|||
|
|
|||
|
<h2 id="création-et-configuration---ynh">Création et configuration - ynh</h2>
|
|||
|
|
|||
|
<h3 id="télécharger-une-image-iso-yunohost-virtualbox">Télécharger une image “iso” yunohost virtualbox</h3>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir ~/yunohost # créer un dossier
|
|||
|
cd ~/yunohost
|
|||
|
# Télécharger la dernière image stable
|
|||
|
wget https://build.yunohost.org/yunohost-stretch-3.6.4.6-amd64-stable.iso
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="créer-ynh">Créer “ynh”</h3>
|
|||
|
|
|||
|
<p>Création “ynh” avec disque dur ynh.vmdk de 10 Go via phpvirtualbox et le lancer</p>
|
|||
|
|
|||
|
<p><img src="/images/phpvbox001.png" alt="" width="500" /></p>
|
|||
|
|
|||
|
<p>Activer la liaison sécurisée SSH/RDP pour installer yunohost</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh -L 15000:localhost:3389 admbust@xoyize.xyz -p 55035 -i /home/yannick/.ssh/vbox-srvbust-ed25519
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Exécuter la commande suivante sur un second terminal pour accès en mode console</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rdesktop -g 1024x768 -P -z -k fr localhost:15000 # Automatic scaling of geometry
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><img src="/images/phpvbox002.png" alt="" width="200" /></p>
|
|||
|
|
|||
|
<h3 id="yunohost">Yunohost</h3>
|
|||
|
|
|||
|
<p>Lancer l’installation de yunohost… <br />
|
|||
|
puis la post installation domaine : cinay.eu<br />
|
|||
|
<img src="/images/ynhvb001.png" alt="" width="400" /></p>
|
|||
|
|
|||
|
<p>Se connecter en <em>admin</em><br />
|
|||
|
Lancer une mise à jour</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt update && sudo apt -y upgrade
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Relever adresse ip : 192.168.0.36 et adresse mac : 08:00:27:3c:82:f2 <br />
|
|||
|
inet6 fe80::a00:27ff:fe3c:82f2/64
|
|||
|
connexion via SSH</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh admin@192.168.0.36
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="locales">Locales</h3>
|
|||
|
|
|||
|
<p>Pour avoir l’interface en français</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -s
|
|||
|
nano /etc/locale.gen
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Supprimer le <code class="language-plaintext highlighter-rouge">#</code> de la ligne fr_FR.UTF-8<br />
|
|||
|
Générer</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>locale-gen
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Generating locales (this might take a while)...
|
|||
|
fr_FR.UTF-8... done
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="freebox">Freebox</h3>
|
|||
|
|
|||
|
<p><strong>DMZ</strong><br />
|
|||
|
<img src="/images/freebox-dmz.png" alt="Texte alternatif" width="600" /></p>
|
|||
|
|
|||
|
<p>A partir des éléments fournis par <code class="language-plaintext highlighter-rouge">ip a</code>, définir un <strong>Next Hop ipv6</strong> (2a01:e34:eebf:df2::/64) sur la freebox <br />
|
|||
|
<img src="/images/freebox-ipv6-ouestline.png" alt="Texte alternatif" width="600" /></p>
|
|||
|
|
|||
|
<p>Créer le <strong>Reverse DNS</strong> <ouestline.net> dans l'espace abonné Free (MA FREEBOX → Reverse DNS)</ouestline.net></p>
|
|||
|
|
|||
|
<h3 id="adressage-ipv6">adressage IPV6</h3>
|
|||
|
|
|||
|
<p>Modifier l’interface réseau <strong>/etc/network/interfaces</strong> pour un adressage IPV6</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># This file describes the network interfaces available on your system
|
|||
|
# and how to activate them. For more information, see interfaces(5).
|
|||
|
|
|||
|
source /etc/network/interfaces.d/*
|
|||
|
|
|||
|
# The loopback network interface
|
|||
|
auto lo
|
|||
|
iface lo inet loopback
|
|||
|
|
|||
|
# The primary network interface
|
|||
|
allow-hotplug enp0s3
|
|||
|
iface enp0s3 inet dhcp
|
|||
|
# This is an autoconfigured IPv6 interface
|
|||
|
#iface enp0s3 inet6 auto
|
|||
|
iface enp0s3 inet6 static
|
|||
|
address 2a01:e34:eebf:df2::1
|
|||
|
netmask 64
|
|||
|
post-up ip -6 route add default via fe80::224:d4ff:fea6:aa20 dev enp0s3
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Et redémarrer <code class="language-plaintext highlighter-rouge">sudo systemctl reboot</code></p>
|
|||
|
|
|||
|
<p>Vérification adressage IPV6</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip a
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
|
|||
|
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
|
|||
|
valid_lft forever preferred_lft forever
|
|||
|
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
|||
|
link/ether 08:00:27:3c:82:f2 brd ff:ff:ff:ff:ff:ff
|
|||
|
inet 192.168.0.36/24 brd 192.168.0.255 scope global enp0s3
|
|||
|
valid_lft forever preferred_lft forever
|
|||
|
inet6 2a01:e34:eebf:df2::1/64 scope global
|
|||
|
valid_lft forever preferred_lft forever
|
|||
|
inet6 2a01:e34:eebf:df0:a00:27ff:fe3c:82f2/64 scope global mngtmpaddr dynamic
|
|||
|
valid_lft 85869sec preferred_lft 85869sec
|
|||
|
inet6 fe80::a00:27ff:fe3c:82f2/64 scope link
|
|||
|
valid_lft forever preferred_lft forever
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="ovh---configuration-dns-ouestlinenet">OVH - Configuration DNS ouestline.net</h3>
|
|||
|
|
|||
|
<font color="green"><b>On fait le choix de définir le domaine ouestline.net en IPV6 uniquement</b></font>
|
|||
|
|
|||
|
<p>Informations yunohost sur le domaine ouestline.net</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>; Basic ipv4/ipv6 records
|
|||
|
@ 3600 IN A 78.235.240.223
|
|||
|
* 3600 IN A 78.235.240.223
|
|||
|
@ 3600 IN AAAA 2a01:e34:eebf:df2::1
|
|||
|
* 3600 IN AAAA 2a01:e34:eebf:df2::1
|
|||
|
|
|||
|
; XMPP
|
|||
|
_xmpp-client._tcp 3600 IN SRV 0 5 5222 ouestline.net.
|
|||
|
_xmpp-server._tcp 3600 IN SRV 0 5 5269 ouestline.net.
|
|||
|
muc 3600 IN CNAME @
|
|||
|
pubsub 3600 IN CNAME @
|
|||
|
vjud 3600 IN CNAME @
|
|||
|
|
|||
|
; Mail
|
|||
|
@ 3600 IN MX 10 ouestline.net.
|
|||
|
@ 3600 IN TXT "v=spf1 a mx ip4:78.235.240.223 ip6:2a01:e34:eebf:df2::1 -all"
|
|||
|
mail._domainkey 3600 IN TXT "v=DKIM1; h=sha256; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOT4EhluaTJNwVu668Wn6+OVhuQQUn94KcAOAXi+xKpjH+LA4g/5p6WUUV1Qo4Vgp9Il90dZKM/vAjilchXB+vePT2XZwqWIvjxTYD6s97E/YBYbEVUJdoCoZlxaQPS+T90ikJJje8vK9SO4AMuQKGxzHeTzM4dHIVDMQCXmQUfQIDAQAB"
|
|||
|
_dmarc 3600 IN TXT "v=DMARC1; p=none"
|
|||
|
|
|||
|
; Extra
|
|||
|
@ 3600 IN CAA 128 issue "letsencrypt.org"
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Paramétrage de la zone DNS OVH ouestline.net</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$TTL 3600
|
|||
|
@ IN SOA dns111.ovh.net. tech.ovh.net. (2020012301 86400 3600 3600000 300)
|
|||
|
IN NS dns111.ovh.net.
|
|||
|
IN NS ns111.ovh.net.
|
|||
|
IN MX 10 ouestline.net.
|
|||
|
IN A 78.235.240.223
|
|||
|
IN AAAA 2a01:e34:eebf:df2::1
|
|||
|
IN CAA 128 issue "letsencrypt.org"
|
|||
|
600 IN TXT "v=spf1 mx ip4:78.235.240.223 ip6:2a01:e34:eebf:df2::1 -all"
|
|||
|
* IN CNAME ouestline.net.
|
|||
|
_dmarc IN TXT "v=DMARC1;p=none;"
|
|||
|
mail._domainkey IN TXT ( "v=DKIM1;h=sha256;k=rsa;s=*;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOT4EhluaTJNwVu668Wn6+OVhuQQUn94KcAOAXi+xKpjH+LA4g/5p6WUUV1Qo4Vgp9Il90dZKM/vAjilchXB+vePT2XZwqWIvjxTYD6s97E/YBYbEVUJdoCoZlxaQPS+T90ikJJje8vK9SO4AMuQKGxzHeTzM4dHIVDMQCXmQUfQIDAQAB;" )
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="dnsmasq">Dnsmasq</h3>
|
|||
|
|
|||
|
<p>Modifier l’adresse IPV6 , dernière ligne du fichier suivant <strong>/etc/dnsmasq.d/ouestline.net</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>address=/ouestline.net/78.235.240.223
|
|||
|
txt-record=ouestline.net,"v=spf1 mx a -all"
|
|||
|
mx-host=ouestline.net,ouestline.net,5
|
|||
|
srv-host=_xmpp-client._tcp.ouestline.net,ouestline.net,5222,0,5
|
|||
|
srv-host=_xmpp-server._tcp.ouestline.net,ouestline.net,5269,0,5
|
|||
|
address=/ouestline.net/2a01:e34:eebf:df2::1
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Relancer</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl restart dnsmasq
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="acme---certificats-letsencrypt">Acme - Certificats letsencrypt</h3>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<font color="red"><b>On ne peut pas utiliser la création des certificats proposé par yunohost car elle utilise l'adressage ipv4</b></font>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p><a href="https://yann.cinay.eu/2017/08/31/Acme-Certficats-Serveurs.html#g%C3%A9n%C3%A9ration-des-certificats-avec-le-client-acmesh">Génération des certificats avec le client acme.sh</a></p>
|
|||
|
|
|||
|
<p>La ligne de commande</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>acme.sh --dns dns_ovh --ocsp --issue --keylength ec-384 -d 'ouestline.net' -d '*.ouestline.net'
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Les certificats sont sous <strong>/home/admin/.acme.sh/ouestline.net_ecc/</strong></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[lundi 13 janvier 2020, 16:39:58 (UTC+0100)] Your cert is in /home/admin/.acme.sh/ouestline.net_ecc/ouestline.net.cer
|
|||
|
[lundi 13 janvier 2020, 16:39:58 (UTC+0100)] Your cert key is in /home/admin/.acme.sh/ouestline.net_ecc/ouestline.net.key
|
|||
|
[lundi 13 janvier 2020, 16:39:58 (UTC+0100)] The intermediate CA cert is in /home/admin/.acme.sh/ouestline.net_ecc/ca.cer
|
|||
|
[lundi 13 janvier 2020, 16:39:58 (UTC+0100)] And the full chain certs is there: /home/admin/.acme.sh/ouestline.net_ecc/fullchain.cer
|
|||
|
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Remplacer les certificats actuels</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo mv /etc/yunohost/certs/ouestline.net/crt.pem /etc/yunohost/certs/ouestline.net/crt.pem.sav
|
|||
|
sudo mv /etc/yunohost/certs/ouestline.net/key.pem /etc/yunohost/certs/ouestline.net/key.pem.sav
|
|||
|
sudo ln -s /home/admin/.acme.sh/ouestline.net_ecc/fullchain.cer /etc/yunohost/certs/ouestline.net/crt.pem
|
|||
|
sudo ln -s /home/admin/.acme.sh/ouestline.net_ecc/ouestline.net.key /etc/yunohost/certs/ouestline.net/key.pem
|
|||
|
sudo systemctl restart nginx
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vérifier par le lien <a href="https://ouestline.net">https://ouestline.net</a> que la connexion est sécurisée<br />
|
|||
|
<img src="/images/ouestline.net-letsencrypt.png" alt="" width="300" /></p>
|
|||
|
|
|||
|
<p>Le renouvellement est fait automatiquement avec le “scheduler” admin</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>crontab -l
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>25 0 * * * "/home/admin/.acme.sh"/acme.sh --cron --home "/home/admin/.acme.sh" > /dev/null
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vérifier le fonctionnement du script</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"/home/admin/.acme.sh"/acme.sh --cron --ocsp --home "/home/admin/.acme.sh"
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[lundi 13 janvier 2020, 16:42:31 (UTC+0100)] ===Starting cron===
|
|||
|
[lundi 13 janvier 2020, 16:42:31 (UTC+0100)] Renew: 'ouestline.net'
|
|||
|
[lundi 13 janvier 2020, 16:42:31 (UTC+0100)] Skip, Next renewal time is: vendredi 13 mars 2020, 15:39:58 (UTC+0000)
|
|||
|
[lundi 13 janvier 2020, 16:42:31 (UTC+0100)] Add '--force' to force to renew.
|
|||
|
[lundi 13 janvier 2020, 16:42:31 (UTC+0100)] Skipped ouestline.net_ecc
|
|||
|
[lundi 13 janvier 2020, 16:42:31 (UTC+0100)] ===End cron===
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="ouestlinenet---dhocsp">ouestline.net - dh+ocsp</h3>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>Les commandes se font en mode su ou sudo</p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p><em><a href="https://fr.wikipedia.org/wiki/%C3%89change_de_cl%C3%A9s_Diffie-Hellman">Wikipédia : Échange de clés Diffie-Hellman</a><br />
|
|||
|
En cryptographie, l’échange de clés Diffie-Hellman, du nom de ses auteurs Whitfield Diffie et Martin Hellman, est une méthode1, publiée en 1976, par laquelle deux agents, nommés par convention Alice et Bob, peuvent se mettre d’accord sur un nombre (qu’ils peuvent utiliser comme clé pour chiffrer la conversation suivante) sans qu’un troisième agent appelé Ève puisse découvrir le nombre, même en ayant écouté tous leurs échanges.</em><br />
|
|||
|
<img src="/images/Diffie-Hellman_Key_Exchange_(fr).svg" alt="Texte alternatif" width="300" /><br />
|
|||
|
<em>Illustration conceptuelle d’un échange de clés Diffie-Hellman</em></p>
|
|||
|
|
|||
|
<p>Générer la clé dh</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -s
|
|||
|
openssl dhparam -out /etc/ssl/private/dh2048.pem -outform PEM -2 2048
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><em><a href="https://fr.wikipedia.org/wiki/Online_Certificate_Status_Protocol">Wikipédia : Online Certificate Status Protocol</a> (OCSP, en français « protocole de vérification de certificat en ligne ») est un protocole Internet utilisé pour valider un certificat numérique X.509. OCSP est standardisé par l’IETF dans la RFC 69601.</em></p>
|
|||
|
|
|||
|
<table>
|
|||
|
<thead>
|
|||
|
<tr>
|
|||
|
<th>comment la requête OCSP est formée et envoyée<br /> vers le serveur OCSP.</th>
|
|||
|
<th>comment la réponse OCSP est formée<br /> et envoyée vers le client OCSP.<br />Cette réponse peut être : GOOD, REVOKED ou UNKNOWN.</th>
|
|||
|
</tr>
|
|||
|
</thead>
|
|||
|
<tbody>
|
|||
|
<tr>
|
|||
|
<td><img src="/images/Ocsp-request.png" alt="Texte alternatif" width="300" /></td>
|
|||
|
<td><img src="/images/Ocsp-request.png" alt="Texte alternatif" width="300" /></td>
|
|||
|
</tr>
|
|||
|
</tbody>
|
|||
|
</table>
|
|||
|
|
|||
|
<p>Modifier le fichier de configuration <strong>/etc/nginx/conf.d/ouestline.net.conf</strong> pour activer dh et ocsp <br />
|
|||
|
Supprimer le <code class="language-plaintext highlighter-rouge">#</code> qui précède la commande <code class="language-plaintext highlighter-rouge">ssl_dhparam /etc/ssl/private/dh2048.pem;</code><br />
|
|||
|
Ajouter ce qui suit la ligne suivante</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> # OCSP settings
|
|||
|
ssl_stapling on;
|
|||
|
ssl_stapling_verify on;
|
|||
|
ssl_trusted_certificate /etc/yunohost/certs/ouestline.net/crt.pem;
|
|||
|
resolver 127.0.0.1;
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vérifier et relancer nginx</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nginx -t
|
|||
|
systemctl restart nginx
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Tester la réponse OCSP</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>openssl s_client -connect ouestline.net:443 -status < /dev/null
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[...]
|
|||
|
OCSP response:
|
|||
|
======================================
|
|||
|
OCSP Response Data:
|
|||
|
OCSP Response Status: successful (0x0)
|
|||
|
Response Type: Basic OCSP Response
|
|||
|
Version: 1 (0x0)
|
|||
|
Responder Id: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
|
|||
|
Produced At: Jan 11 07:58:00 2020 GMT
|
|||
|
Responses:
|
|||
|
Certificate ID:
|
|||
|
Hash Algorithm: sha1
|
|||
|
Issuer Name Hash: 7729AB3FCF8A22064A12DEE66AE76071085D6C16
|
|||
|
Issuer Key Hash: ADDDBAE6D139B74565EFF384A6A63047A8ECA1A6
|
|||
|
Serial Number: 60B5D00F31795538F9C2C9A60354993266A5
|
|||
|
Cert Status: good
|
|||
|
[...]
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="openssl-version-111x-non-implante">Openssl version 1.1.1x (NON IMPLANTE)</h3>
|
|||
|
|
|||
|
<p>Pour installer une version openssl plus récente et PHP7.x</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Nouvelle source pour installer Openssl , PHP7
|
|||
|
apt-get -y install apt-transport-https lsb-release ca-certificates
|
|||
|
wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
|
|||
|
sh -c 'echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'
|
|||
|
apt update && apt upgrade -y
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="additions-invité-nécessaire-pour-le-partage">Additions invité (nécessaire pour le partage)</h3>
|
|||
|
|
|||
|
<p>Ouvrir <strong>phpvirtualbox</strong> et sélectionner <em>stockage</em> de la machine virtuelle ‘ynh’<br />
|
|||
|
Insérer le cdrom contenant l’image <strong>VBoxGuestAdditions.iso</strong> (située généralement sous <strong>/usr/share/virtualbox</strong>)</p>
|
|||
|
|
|||
|
<p><img src="/images/virtualbox-additions-clients.png" alt="" width="600" /><br />
|
|||
|
<img src="/images/virtualbox-additions-clients-1.png" alt="" width="300" /></p>
|
|||
|
|
|||
|
<p>Ajouter un partage qui sera pris en compte au prochain démarrage<br />
|
|||
|
<img src="/images/virtualbox-additions-clients-2.png" alt="" width="200" /></p>
|
|||
|
|
|||
|
<p>Connexion sur la machine virtuelle ‘ynh’ : <code class="language-plaintext highlighter-rouge">ssh admin@192.168.0.36</code><br />
|
|||
|
Installer les additions client dans un Debian en cours d’exécution dans une machine virtuelle.</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Installez gcc ,make et kernel headers (installateur a besoin d’eux pour construire le module du noyau):<code class="language-plaintext highlighter-rouge">sudo apt install gcc make linux-headers-$(uname -r)</code></li>
|
|||
|
<li>Monter le cd dans la machine virtuelle : <code class="language-plaintext highlighter-rouge">sudo mount /dev/cdrom /media/cdrom</code></li>
|
|||
|
<li>Allez dans le dossier monté : <code class="language-plaintext highlighter-rouge">cd /media/cdrom</code></li>
|
|||
|
<li>Exécutez : <code class="language-plaintext highlighter-rouge">sudo ./VBoxLinuxAdditions.run</code> , patienter quelques minutes…</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>Redémarrer la machine virtuelle : <code class="language-plaintext highlighter-rouge">sudo systemctl reboot</code></p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<h2 id="sécurité">Sécurité</h2>
|
|||
|
|
|||
|
<h3 id="authentification-ssh-par-clé">Authentification SSH par clé</h3>
|
|||
|
|
|||
|
<p>Sur le <u>poste appelant</u>, générer un jeu de clés</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh-keygen -t ed25519 -o -a 100 -f ouestline
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Copier la clé publique sur le serveur ynh</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh-copy-id -i ouestline.pub admin@192.168.0.36
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Sur le <u>serveur ynh</u>, éditez le fichier de configuration SSH, pour désactiver l’authentification par mot de passe et changer de port</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/ssh/sshd_config
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>port 55032
|
|||
|
PasswordAuthentication no
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Sauvegardez et relancez le démon SSH</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl restart ssh
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Activer et désactiver les ports dans le pare feu</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo yunohost firewall allow TCP 55032 # activation port 55032
|
|||
|
sudo yunohost firewall disallow TCP 22 # désactivation port 22
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="désactivation-de-lapi-yunohost">Désactivation de l’API YunoHost</h3>
|
|||
|
|
|||
|
<p>YunoHost est administrable via une API HTTP, servie sur le port 6787 par défaut (seulement sur localhost). Elle permet d’administrer une grande partie de votre serveur, et peut donc être utilisée à des fins malveillantes. La meilleure chose à faire si vous êtes habitués aux lignes de commande est de désactiver le service yunohost-api, et utiliser la ligne de commande en SSH.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl disable yunohost-api
|
|||
|
sudo systemctl stop yunohost-api
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="modifications-des-mots-de-passe">Modifications des mots de passe</h3>
|
|||
|
|
|||
|
<p>En ligne de commande</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh admin@192.168.0.36 -p 55032 -i /home/yannick/.ssh/ouestline # connexion SSH
|
|||
|
sudo -s
|
|||
|
yunohost tools adminpw -n Nouveau_mot_de_passe # modifier mot de passe administrateur
|
|||
|
yunohost user update ouest -p Nouveau_mot_de_passe # modifier mot de passe utilisateur
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="vérifications">Vérifications</h3>
|
|||
|
|
|||
|
<p>Accès ipv4 ipv6</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ping4 -c3 ouestline.net
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PING ouestline.net (78.235.240.223) 56(84) bytes of data.
|
|||
|
64 bytes from smm49-1-78-235-240-223.fbx.proxad.net (78.235.240.223): icmp_seq=1 ttl=49 time=16.2 ms
|
|||
|
64 bytes from smm49-1-78-235-240-223.fbx.proxad.net (78.235.240.223): icmp_seq=2 ttl=49 time=15.9 ms
|
|||
|
64 bytes from smm49-1-78-235-240-223.fbx.proxad.net (78.235.240.223): icmp_seq=3 ttl=49 time=17.5 ms
|
|||
|
|
|||
|
--- ouestline.net ping statistics ---
|
|||
|
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
|
|||
|
rtt min/avg/max/mdev = 15.959/16.592/17.556/0.708 ms
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ping6 -c3 ouestline.net
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PING ouestline.net(2a01:e34:eebf:df2::1 (2a01:e34:eebf:df2::1)) 56 data bytes
|
|||
|
64 bytes from 2a01:e34:eebf:df2::1 (2a01:e34:eebf:df2::1): icmp_seq=1 ttl=51 time=16.8 ms
|
|||
|
64 bytes from 2a01:e34:eebf:df2::1 (2a01:e34:eebf:df2::1): icmp_seq=2 ttl=51 time=16.7 ms
|
|||
|
64 bytes from 2a01:e34:eebf:df2::1 (2a01:e34:eebf:df2::1): icmp_seq=3 ttl=51 time=32.2 ms
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Reverse DNS</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dig A ouestline.net +noall +answer
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>; <<>> DiG 9.11.5-P4-5.1-Debian <<>> A ouestline.net +noall +answer
|
|||
|
;; global options: +cmd
|
|||
|
ouestline.net. 3600 IN A 78.235.240.223
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><a href="https://whatismyipaddress.com/blacklist-check">Blacklist Check</a> (vérifier si l’adresse IP 78.235.240.223 n’est pas dans une liste noire)</p>
|
|||
|
|
|||
|
<h2 id="applications---ynh">Applications - ynh</h2>
|
|||
|
|
|||
|
<h3 id="rainloop">RainLoop</h3>
|
|||
|
|
|||
|
<p><em>RainLoop est un client de messagerie web libre pour le protocole IMAP</em><br />
|
|||
|
Installation via yunohost admin</p>
|
|||
|
|
|||
|
<h3 id="transmission">Transmission</h3>
|
|||
|
|
|||
|
<p><em>Transmission est un client BitTorrent intuitif, fonctionnel et très léger. Il dispose de fonctionnalités comme le chiffrement des échanges de données, l’échange de pairs, le support des blocklists, la limitation des débits en émission et en réception, etc…</em><br />
|
|||
|
Installation via yunohost admin</p>
|
|||
|
|
|||
|
<h3 id="firefox-sync-server">Firefox Sync Server</h3>
|
|||
|
|
|||
|
<p><em>Le serveur de synchronisation de Mozilla, pour héberger vos données Firefox</em><br />
|
|||
|
Installation en ligne de commande</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -s
|
|||
|
yunohost app install https://github.com/YunoHost-Apps/ffsync_ynh
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>AVERTISSEMENT ! L'installation d'applications tierces peut compromettre l'intégrité et la sécurité de votre système. Vous ne devriez probablement PAS l'installer si vous ne savez pas ce que vous faites. Êtes-vous prêt à prendre ce risque ? [Y/N] : Y
|
|||
|
Domaines disponibles :
|
|||
|
- ouestline.net
|
|||
|
Choisissez un domaine pour Firefox-Sync (default: ouestline.net) :
|
|||
|
Choisissez un chemin pour Firefox-Sync (default: /ffsync) :
|
|||
|
Info : Installation de l'application ffsync …
|
|||
|
Info : [....................] > Validating installation parameters...
|
|||
|
Info : [++++++..............] > Installing dependencies...
|
|||
|
Info : [######+.............] > Configuring MySQL database...
|
|||
|
Info : [#######++++++++.....] > Installing sources files...
|
|||
|
Attention : DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
|
|||
|
[...]
|
|||
|
|
|||
|
Info : [###############+....] > Configuring nginx
|
|||
|
Info : [################....] > Configuring system user...
|
|||
|
Info : [################+...] > Configuring application...
|
|||
|
Attention : Created symlink /etc/systemd/system/multi-user.target.wants/uwsgi-app@ffsync.service → /etc/systemd/system/uwsgi-app@.service.
|
|||
|
Info : [#################+..] > Protecting directory
|
|||
|
Info : [##################+.] > Configuring permissions
|
|||
|
Info : [####################] > Installation of ffsync completed
|
|||
|
Succès ! La configuration de SSOwat a été générée
|
|||
|
Succès ! Installation terminée
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Lancer l’application FFsync depuis le tableau de bord yunohost</p>
|
|||
|
|
|||
|
<p><img src="/images/ffouest001.png" alt="" width="600" /></p>
|
|||
|
|
|||
|
<p>Procéder à la modification (comme indiqué)<br />
|
|||
|
Pour configurer Firefox pour qu’il parle à votre nouveau serveur Sync, allez dans “<code class="language-plaintext highlighter-rouge">about:config</code>”, cherchez “<code class="language-plaintext highlighter-rouge">identity.sync.tokenserver.uri</code>” et changez sa valeur en URL de votre serveur avec un chemin d’accès de “token/1.0/sync/1.5” :<br />
|
|||
|
<code class="language-plaintext highlighter-rouge">identity.sync.tokenserver.uri</code> → https://ouestline.net/ffsync/token/1.0/sync/1.5 <br />
|
|||
|
<img src="/images/ffouest003.png" alt="" width="400" /></p>
|
|||
|
|
|||
|
<p>Puis se rendre dans “Préférences → Sync”<br />
|
|||
|
<img src="/images/ffouest002.png" alt="" width="400" /></p>
|
|||
|
|
|||
|
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<div class="d-print-none"><footer class="article__footer"><meta itemprop="dateModified" content="2019-12-28T00:00:00+01:00"><!-- start custom article footer snippet -->
|
|||
|
|
|||
|
<!-- end custom article footer snippet -->
|
|||
|
<!--
|
|||
|
<div align="right"><a type="application/rss+xml" href="/feed.xml" title="S'abonner"><i class="fa fa-rss fa-2x"></i></a>
|
|||
|
|
|||
|
 </div>
|
|||
|
-->
|
|||
|
</footer>
|
|||
|
<div class="article__section-navigator clearfix"><div class="previous"><span>PRÉCÉDENT</span><a href="/2019/12/28/Archlinux-Debian-Node.js-Nvm-Npm-Yarn.html">Archlinux Debian , installation des paquets node npm nvm yarn</a></div><div class="next"><span>SUIVANT</span><a href="/2019/12/30/Archlinux-yay-un-yaourt-AUR-Helper-en-Go.html">Archlinux "yay" un autre "yaourt" - Un AUR Helper écrit en Go</a></div></div></div>
|
|||
|
|
|||
|
</div>
|
|||
|
|
|||
|
<script>(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
$(function() {
|
|||
|
var $this ,$scroll;
|
|||
|
var $articleContent = $('.js-article-content');
|
|||
|
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
|
|||
|
var scroll = hasSidebar ? '.js-page-main' : 'html, body';
|
|||
|
$scroll = $(scroll);
|
|||
|
|
|||
|
$articleContent.find('.highlight').each(function() {
|
|||
|
$this = $(this);
|
|||
|
$this.attr('data-lang', $this.find('code').attr('data-lang'));
|
|||
|
});
|
|||
|
$articleContent.find('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]').each(function() {
|
|||
|
$this = $(this);
|
|||
|
$this.append($('<a class="anchor d-print-none" aria-hidden="true"></a>').html('<i class="fas fa-anchor"></i>'));
|
|||
|
});
|
|||
|
$articleContent.on('click', '.anchor', function() {
|
|||
|
$scroll.scrollToAnchor('#' + $(this).parent().attr('id'), 400);
|
|||
|
});
|
|||
|
});
|
|||
|
});
|
|||
|
})();
|
|||
|
</script>
|
|||
|
|
|||
|
</div><section class="page__comments d-print-none"></section></article><!-- start custom main bottom snippet -->
|
|||
|
<!-- end custom main bottom snippet -->
|
|||
|
</div>
|
|||
|
</div></div></div></div>
|
|||
|
</div><script>(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
var $body = $('body'), $window = $(window);
|
|||
|
var $pageRoot = $('.js-page-root'), $pageMain = $('.js-page-main');
|
|||
|
var activeCount = 0;
|
|||
|
function modal(options) {
|
|||
|
var $root = this, visible, onChange, hideWhenWindowScroll = false;
|
|||
|
var scrollTop;
|
|||
|
function setOptions(options) {
|
|||
|
var _options = options || {};
|
|||
|
visible = _options.initialVisible === undefined ? false : show;
|
|||
|
onChange = _options.onChange;
|
|||
|
hideWhenWindowScroll = _options.hideWhenWindowScroll;
|
|||
|
}
|
|||
|
function init() {
|
|||
|
setState(visible);
|
|||
|
}
|
|||
|
function setState(isShow) {
|
|||
|
if (isShow === visible) {
|
|||
|
return;
|
|||
|
}
|
|||
|
visible = isShow;
|
|||
|
if (visible) {
|
|||
|
activeCount++;
|
|||
|
scrollTop = $(window).scrollTop() || $pageMain.scrollTop();
|
|||
|
$root.addClass('modal--show');
|
|||
|
$pageMain.scrollTop(scrollTop);
|
|||
|
activeCount === 1 && ($pageRoot.addClass('show-modal'), $body.addClass('of-hidden'));
|
|||
|
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.on('scroll', hide);
|
|||
|
$window.on('keyup', handleKeyup);
|
|||
|
} else {
|
|||
|
activeCount > 0 && activeCount--;
|
|||
|
$root.removeClass('modal--show');
|
|||
|
$window.scrollTop(scrollTop);
|
|||
|
activeCount === 0 && ($pageRoot.removeClass('show-modal'), $body.removeClass('of-hidden'));
|
|||
|
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.off('scroll', hide);
|
|||
|
$window.off('keyup', handleKeyup);
|
|||
|
}
|
|||
|
onChange && onChange(visible);
|
|||
|
}
|
|||
|
function show() {
|
|||
|
setState(true);
|
|||
|
}
|
|||
|
function hide() {
|
|||
|
setState(false);
|
|||
|
}
|
|||
|
function handleKeyup(e) {
|
|||
|
// Char Code: 27 ESC
|
|||
|
if (e.which === 27) {
|
|||
|
hide();
|
|||
|
}
|
|||
|
}
|
|||
|
setOptions(options);
|
|||
|
init();
|
|||
|
return {
|
|||
|
show: show,
|
|||
|
hide: hide,
|
|||
|
$el: $root
|
|||
|
};
|
|||
|
}
|
|||
|
$.fn.modal = modal;
|
|||
|
});
|
|||
|
})();
|
|||
|
</script><div class="modal modal--overflow page__search-modal d-print-none js-page-search-modal"><script>
|
|||
|
(function () {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
// search panel
|
|||
|
var search = (window.search || (window.search = {}));
|
|||
|
var useDefaultSearchBox = window.useDefaultSearchBox === undefined ?
|
|||
|
true : window.useDefaultSearchBox ;
|
|||
|
|
|||
|
var $searchModal = $('.js-page-search-modal');
|
|||
|
var $searchToggle = $('.js-search-toggle');
|
|||
|
var searchModal = $searchModal.modal({ onChange: handleModalChange, hideWhenWindowScroll: true });
|
|||
|
var modalVisible = false;
|
|||
|
search.searchModal = searchModal;
|
|||
|
|
|||
|
var $searchBox = null;
|
|||
|
var $searchInput = null;
|
|||
|
var $searchClear = null;
|
|||
|
|
|||
|
function getModalVisible() {
|
|||
|
return modalVisible;
|
|||
|
}
|
|||
|
search.getModalVisible = getModalVisible;
|
|||
|
|
|||
|
function handleModalChange(visible) {
|
|||
|
modalVisible = visible;
|
|||
|
if (visible) {
|
|||
|
search.onShow && search.onShow();
|
|||
|
useDefaultSearchBox && $searchInput[0] && $searchInput[0].focus();
|
|||
|
} else {
|
|||
|
search.onShow && search.onHide();
|
|||
|
useDefaultSearchBox && $searchInput[0] && $searchInput[0].blur();
|
|||
|
setTimeout(function() {
|
|||
|
useDefaultSearchBox && ($searchInput.val(''), $searchBox.removeClass('not-empty'));
|
|||
|
search.clear && search.clear();
|
|||
|
window.pageAsideAffix && window.pageAsideAffix.refresh();
|
|||
|
}, 400);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
$searchToggle.on('click', function() {
|
|||
|
modalVisible ? searchModal.hide() : searchModal.show();
|
|||
|
});
|
|||
|
// Char Code: 83 S, 191 /
|
|||
|
$(window).on('keyup', function(e) {
|
|||
|
if (!modalVisible && !window.isFormElement(e.target || e.srcElement) && (e.which === 83 || e.which === 191)) {
|
|||
|
modalVisible || searchModal.show();
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
if (useDefaultSearchBox) {
|
|||
|
$searchBox = $('.js-search-box');
|
|||
|
$searchInput = $searchBox.children('input');
|
|||
|
$searchClear = $searchBox.children('.js-icon-clear');
|
|||
|
search.getSearchInput = function() {
|
|||
|
return $searchInput.get(0);
|
|||
|
};
|
|||
|
search.getVal = function() {
|
|||
|
return $searchInput.val();
|
|||
|
};
|
|||
|
search.setVal = function(val) {
|
|||
|
$searchInput.val(val);
|
|||
|
};
|
|||
|
|
|||
|
$searchInput.on('focus', function() {
|
|||
|
$(this).addClass('focus');
|
|||
|
});
|
|||
|
$searchInput.on('blur', function() {
|
|||
|
$(this).removeClass('focus');
|
|||
|
});
|
|||
|
$searchInput.on('input', window.throttle(function() {
|
|||
|
var val = $(this).val();
|
|||
|
if (val === '' || typeof val !== 'string') {
|
|||
|
search.clear && search.clear();
|
|||
|
} else {
|
|||
|
$searchBox.addClass('not-empty');
|
|||
|
search.onInputNotEmpty && search.onInputNotEmpty(val);
|
|||
|
}
|
|||
|
}, 400));
|
|||
|
$searchClear.on('click', function() {
|
|||
|
$searchInput.val(''); $searchBox.removeClass('not-empty');
|
|||
|
search.clear && search.clear();
|
|||
|
});
|
|||
|
}
|
|||
|
});
|
|||
|
})();
|
|||
|
</script><div class="search search--dark">
|
|||
|
<div class="main">
|
|||
|
<div class="search__header">Recherche</div>
|
|||
|
<div class="search-bar">
|
|||
|
<div class="search-box js-search-box">
|
|||
|
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
|
|||
|
<input id="search-input" type="text" />
|
|||
|
<div class="search-box__icon-clear js-icon-clear">
|
|||
|
<a><i class="fas fa-times"></i></a>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<button class="button button--theme-dark button--pill search__cancel js-search-toggle">
|
|||
|
Annuler</button>
|
|||
|
</div>
|
|||
|
<div id="results-container" class="search-result js-search-result"></div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<!-- Script pointing to search-script.js -->
|
|||
|
<script>/*!
|
|||
|
* Simple-Jekyll-Search
|
|||
|
* Copyright 2015-2020, Christian Fei
|
|||
|
* Licensed under the MIT License.
|
|||
|
*/
|
|||
|
|
|||
|
(function(){
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$Templater_7 = {
|
|||
|
compile: compile,
|
|||
|
setOptions: setOptions
|
|||
|
}
|
|||
|
|
|||
|
const options = {}
|
|||
|
options.pattern = /\{(.*?)\}/g
|
|||
|
options.template = ''
|
|||
|
options.middleware = function () {}
|
|||
|
|
|||
|
function setOptions (_options) {
|
|||
|
options.pattern = _options.pattern || options.pattern
|
|||
|
options.template = _options.template || options.template
|
|||
|
if (typeof _options.middleware === 'function') {
|
|||
|
options.middleware = _options.middleware
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function compile (data) {
|
|||
|
return options.template.replace(options.pattern, function (match, prop) {
|
|||
|
const value = options.middleware(prop, data[prop], options.template)
|
|||
|
if (typeof value !== 'undefined') {
|
|||
|
return value
|
|||
|
}
|
|||
|
return data[prop] || match
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
'use strict';
|
|||
|
|
|||
|
function fuzzysearch (needle, haystack) {
|
|||
|
var tlen = haystack.length;
|
|||
|
var qlen = needle.length;
|
|||
|
if (qlen > tlen) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
if (qlen === tlen) {
|
|||
|
return needle === haystack;
|
|||
|
}
|
|||
|
outer: for (var i = 0, j = 0; i < qlen; i++) {
|
|||
|
var nch = needle.charCodeAt(i);
|
|||
|
while (j < tlen) {
|
|||
|
if (haystack.charCodeAt(j++) === nch) {
|
|||
|
continue outer;
|
|||
|
}
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
var _$fuzzysearch_1 = fuzzysearch;
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
|
|||
|
|
|||
|
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
|
|||
|
|
|||
|
function FuzzySearchStrategy () {
|
|||
|
this.matches = function (string, crit) {
|
|||
|
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
|
|||
|
|
|||
|
function LiteralSearchStrategy () {
|
|||
|
this.matches = function (str, crit) {
|
|||
|
if (!str) return false
|
|||
|
|
|||
|
str = str.trim().toLowerCase()
|
|||
|
crit = crit.trim().toLowerCase()
|
|||
|
|
|||
|
return crit.split(' ').filter(function (word) {
|
|||
|
return str.indexOf(word) >= 0
|
|||
|
}).length === crit.split(' ').length
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$Repository_4 = {
|
|||
|
put: put,
|
|||
|
clear: clear,
|
|||
|
search: search,
|
|||
|
setOptions: __setOptions_4
|
|||
|
}
|
|||
|
|
|||
|
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
|
|||
|
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
|
|||
|
|
|||
|
function NoSort () {
|
|||
|
return 0
|
|||
|
}
|
|||
|
|
|||
|
const data = []
|
|||
|
let opt = {}
|
|||
|
|
|||
|
opt.fuzzy = false
|
|||
|
opt.limit = 10
|
|||
|
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
|
|||
|
opt.sort = NoSort
|
|||
|
opt.exclude = []
|
|||
|
|
|||
|
function put (data) {
|
|||
|
if (isObject(data)) {
|
|||
|
return addObject(data)
|
|||
|
}
|
|||
|
if (isArray(data)) {
|
|||
|
return addArray(data)
|
|||
|
}
|
|||
|
return undefined
|
|||
|
}
|
|||
|
function clear () {
|
|||
|
data.length = 0
|
|||
|
return data
|
|||
|
}
|
|||
|
|
|||
|
function isObject (obj) {
|
|||
|
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
|
|||
|
}
|
|||
|
|
|||
|
function isArray (obj) {
|
|||
|
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
|
|||
|
}
|
|||
|
|
|||
|
function addObject (_data) {
|
|||
|
data.push(_data)
|
|||
|
return data
|
|||
|
}
|
|||
|
|
|||
|
function addArray (_data) {
|
|||
|
const added = []
|
|||
|
clear()
|
|||
|
for (let i = 0, len = _data.length; i < len; i++) {
|
|||
|
if (isObject(_data[i])) {
|
|||
|
added.push(addObject(_data[i]))
|
|||
|
}
|
|||
|
}
|
|||
|
return added
|
|||
|
}
|
|||
|
|
|||
|
function search (crit) {
|
|||
|
if (!crit) {
|
|||
|
return []
|
|||
|
}
|
|||
|
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
|
|||
|
}
|
|||
|
|
|||
|
function __setOptions_4 (_opt) {
|
|||
|
opt = _opt || {}
|
|||
|
|
|||
|
opt.fuzzy = _opt.fuzzy || false
|
|||
|
opt.limit = _opt.limit || 10
|
|||
|
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
|
|||
|
opt.sort = _opt.sort || NoSort
|
|||
|
opt.exclude = _opt.exclude || []
|
|||
|
}
|
|||
|
|
|||
|
function findMatches (data, crit, strategy, opt) {
|
|||
|
const matches = []
|
|||
|
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
|
|||
|
const match = findMatchesInObject(data[i], crit, strategy, opt)
|
|||
|
if (match) {
|
|||
|
matches.push(match)
|
|||
|
}
|
|||
|
}
|
|||
|
return matches
|
|||
|
}
|
|||
|
|
|||
|
function findMatchesInObject (obj, crit, strategy, opt) {
|
|||
|
for (const key in obj) {
|
|||
|
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
|
|||
|
return obj
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function isExcluded (term, excludedTerms) {
|
|||
|
for (let i = 0, len = excludedTerms.length; i < len; i++) {
|
|||
|
const excludedTerm = excludedTerms[i]
|
|||
|
if (new RegExp(excludedTerm).test(term)) {
|
|||
|
return true
|
|||
|
}
|
|||
|
}
|
|||
|
return false
|
|||
|
}
|
|||
|
|
|||
|
/* globals ActiveXObject:false */
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$JSONLoader_2 = {
|
|||
|
load: load
|
|||
|
}
|
|||
|
|
|||
|
function load (location, callback) {
|
|||
|
const xhr = getXHR()
|
|||
|
xhr.open('GET', location, true)
|
|||
|
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
|
|||
|
xhr.send()
|
|||
|
}
|
|||
|
|
|||
|
function createStateChangeListener (xhr, callback) {
|
|||
|
return function () {
|
|||
|
if (xhr.readyState === 4 && xhr.status === 200) {
|
|||
|
try {
|
|||
|
callback(null, JSON.parse(xhr.responseText))
|
|||
|
} catch (err) {
|
|||
|
callback(err, null)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function getXHR () {
|
|||
|
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$OptionsValidator_3 = function OptionsValidator (params) {
|
|||
|
if (!validateParams(params)) {
|
|||
|
throw new Error('-- OptionsValidator: required options missing')
|
|||
|
}
|
|||
|
|
|||
|
if (!(this instanceof OptionsValidator)) {
|
|||
|
return new OptionsValidator(params)
|
|||
|
}
|
|||
|
|
|||
|
const requiredOptions = params.required
|
|||
|
|
|||
|
this.getRequiredOptions = function () {
|
|||
|
return requiredOptions
|
|||
|
}
|
|||
|
|
|||
|
this.validate = function (parameters) {
|
|||
|
const errors = []
|
|||
|
requiredOptions.forEach(function (requiredOptionName) {
|
|||
|
if (typeof parameters[requiredOptionName] === 'undefined') {
|
|||
|
errors.push(requiredOptionName)
|
|||
|
}
|
|||
|
})
|
|||
|
return errors
|
|||
|
}
|
|||
|
|
|||
|
function validateParams (params) {
|
|||
|
if (!params) {
|
|||
|
return false
|
|||
|
}
|
|||
|
return typeof params.required !== 'undefined' && params.required instanceof Array
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$utils_9 = {
|
|||
|
merge: merge,
|
|||
|
isJSON: isJSON
|
|||
|
}
|
|||
|
|
|||
|
function merge (defaultParams, mergeParams) {
|
|||
|
const mergedOptions = {}
|
|||
|
for (const option in defaultParams) {
|
|||
|
mergedOptions[option] = defaultParams[option]
|
|||
|
if (typeof mergeParams[option] !== 'undefined') {
|
|||
|
mergedOptions[option] = mergeParams[option]
|
|||
|
}
|
|||
|
}
|
|||
|
return mergedOptions
|
|||
|
}
|
|||
|
|
|||
|
function isJSON (json) {
|
|||
|
try {
|
|||
|
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
|
|||
|
return true
|
|||
|
}
|
|||
|
return false
|
|||
|
} catch (err) {
|
|||
|
return false
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var _$src_8 = {};
|
|||
|
(function (window) {
|
|||
|
'use strict'
|
|||
|
|
|||
|
let options = {
|
|||
|
searchInput: null,
|
|||
|
resultsContainer: null,
|
|||
|
json: [],
|
|||
|
success: Function.prototype,
|
|||
|
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
|
|||
|
templateMiddleware: Function.prototype,
|
|||
|
sortMiddleware: function () {
|
|||
|
return 0
|
|||
|
},
|
|||
|
noResultsText: 'No results found',
|
|||
|
limit: 10,
|
|||
|
fuzzy: false,
|
|||
|
debounceTime: null,
|
|||
|
exclude: []
|
|||
|
}
|
|||
|
|
|||
|
let debounceTimerHandle
|
|||
|
const debounce = function (func, delayMillis) {
|
|||
|
if (delayMillis) {
|
|||
|
clearTimeout(debounceTimerHandle)
|
|||
|
debounceTimerHandle = setTimeout(func, delayMillis)
|
|||
|
} else {
|
|||
|
func.call()
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
|
|||
|
|
|||
|
/* removed: const _$Templater_7 = require('./Templater') */;
|
|||
|
/* removed: const _$Repository_4 = require('./Repository') */;
|
|||
|
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
|
|||
|
const optionsValidator = _$OptionsValidator_3({
|
|||
|
required: requiredOptions
|
|||
|
})
|
|||
|
/* removed: const _$utils_9 = require('./utils') */;
|
|||
|
|
|||
|
window.SimpleJekyllSearch = function (_options) {
|
|||
|
const errors = optionsValidator.validate(_options)
|
|||
|
if (errors.length > 0) {
|
|||
|
throwError('You must specify the following required options: ' + requiredOptions)
|
|||
|
}
|
|||
|
|
|||
|
options = _$utils_9.merge(options, _options)
|
|||
|
|
|||
|
_$Templater_7.setOptions({
|
|||
|
template: options.searchResultTemplate,
|
|||
|
middleware: options.templateMiddleware
|
|||
|
})
|
|||
|
|
|||
|
_$Repository_4.setOptions({
|
|||
|
fuzzy: options.fuzzy,
|
|||
|
limit: options.limit,
|
|||
|
sort: options.sortMiddleware,
|
|||
|
exclude: options.exclude
|
|||
|
})
|
|||
|
|
|||
|
if (_$utils_9.isJSON(options.json)) {
|
|||
|
initWithJSON(options.json)
|
|||
|
} else {
|
|||
|
initWithURL(options.json)
|
|||
|
}
|
|||
|
|
|||
|
const rv = {
|
|||
|
search: search
|
|||
|
}
|
|||
|
|
|||
|
typeof options.success === 'function' && options.success.call(rv)
|
|||
|
return rv
|
|||
|
}
|
|||
|
|
|||
|
function initWithJSON (json) {
|
|||
|
_$Repository_4.put(json)
|
|||
|
registerInput()
|
|||
|
}
|
|||
|
|
|||
|
function initWithURL (url) {
|
|||
|
_$JSONLoader_2.load(url, function (err, json) {
|
|||
|
if (err) {
|
|||
|
throwError('failed to get JSON (' + url + ')')
|
|||
|
}
|
|||
|
initWithJSON(json)
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
function emptyResultsContainer () {
|
|||
|
options.resultsContainer.innerHTML = ''
|
|||
|
}
|
|||
|
|
|||
|
function appendToResultsContainer (text) {
|
|||
|
options.resultsContainer.innerHTML += text
|
|||
|
}
|
|||
|
|
|||
|
function registerInput () {
|
|||
|
options.searchInput.addEventListener('input', function (e) {
|
|||
|
if (isWhitelistedKey(e.which)) {
|
|||
|
emptyResultsContainer()
|
|||
|
debounce(function () { search(e.target.value) }, options.debounceTime)
|
|||
|
}
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
function search (query) {
|
|||
|
if (isValidQuery(query)) {
|
|||
|
emptyResultsContainer()
|
|||
|
render(_$Repository_4.search(query), query)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function render (results, query) {
|
|||
|
const len = results.length
|
|||
|
if (len === 0) {
|
|||
|
return appendToResultsContainer(options.noResultsText)
|
|||
|
}
|
|||
|
for (let i = 0; i < len; i++) {
|
|||
|
results[i].query = query
|
|||
|
appendToResultsContainer(_$Templater_7.compile(results[i]))
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function isValidQuery (query) {
|
|||
|
return query && query.length > 0
|
|||
|
}
|
|||
|
|
|||
|
function isWhitelistedKey (key) {
|
|||
|
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
|
|||
|
}
|
|||
|
|
|||
|
function throwError (message) {
|
|||
|
throw new Error('SimpleJekyllSearch --- ' + message)
|
|||
|
}
|
|||
|
})(window)
|
|||
|
|
|||
|
}());
|
|||
|
</script>
|
|||
|
|
|||
|
<!-- Configuration -->
|
|||
|
<script>
|
|||
|
SimpleJekyllSearch({
|
|||
|
searchInput: document.getElementById('search-input'),
|
|||
|
resultsContainer: document.getElementById('results-container'),
|
|||
|
noResultsText: '<p>Aucun résultat!</p>',
|
|||
|
json: '/search.json',
|
|||
|
searchResultTemplate: '<li><a href="{url}">{date} {title}</a> (Création {create})</li>'
|
|||
|
})
|
|||
|
</script>
|
|||
|
|
|||
|
</div></div>
|
|||
|
|
|||
|
|
|||
|
<script>(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
function scrollToAnchor(anchor, duration, callback) {
|
|||
|
var $root = this;
|
|||
|
$root.animate({ scrollTop: $(anchor).position().top }, duration, function() {
|
|||
|
window.history.replaceState(null, '', window.location.href.split('#')[0] + anchor);
|
|||
|
callback && callback();
|
|||
|
});
|
|||
|
}
|
|||
|
$.fn.scrollToAnchor = scrollToAnchor;
|
|||
|
});
|
|||
|
})();
|
|||
|
(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
function affix(options) {
|
|||
|
var $root = this, $window = $(window), $scrollTarget, $scroll,
|
|||
|
offsetBottom = 0, scrollTarget = window, scroll = window.document, disabled = false, isOverallScroller = true,
|
|||
|
rootTop, rootLeft, rootHeight, scrollBottom, rootBottomTop,
|
|||
|
hasInit = false, curState;
|
|||
|
|
|||
|
function setOptions(options) {
|
|||
|
var _options = options || {};
|
|||
|
_options.offsetBottom && (offsetBottom = _options.offsetBottom);
|
|||
|
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
|
|||
|
_options.scroll && (scroll = _options.scroll);
|
|||
|
_options.disabled !== undefined && (disabled = _options.disabled);
|
|||
|
$scrollTarget = $(scrollTarget);
|
|||
|
isOverallScroller = window.isOverallScroller($scrollTarget[0]);
|
|||
|
$scroll = $(scroll);
|
|||
|
}
|
|||
|
function preCalc() {
|
|||
|
top();
|
|||
|
rootHeight = $root.outerHeight();
|
|||
|
rootTop = $root.offset().top + (isOverallScroller ? 0 : $scrollTarget.scrollTop());
|
|||
|
rootLeft = $root.offset().left;
|
|||
|
}
|
|||
|
function calc(needPreCalc) {
|
|||
|
needPreCalc && preCalc();
|
|||
|
scrollBottom = $scroll.outerHeight() - offsetBottom - rootHeight;
|
|||
|
rootBottomTop = scrollBottom - rootTop;
|
|||
|
}
|
|||
|
function top() {
|
|||
|
if (curState !== 'top') {
|
|||
|
$root.removeClass('fixed').css({
|
|||
|
left: 0,
|
|||
|
top: 0
|
|||
|
});
|
|||
|
curState = 'top';
|
|||
|
}
|
|||
|
}
|
|||
|
function fixed() {
|
|||
|
if (curState !== 'fixed') {
|
|||
|
$root.addClass('fixed').css({
|
|||
|
left: rootLeft + 'px',
|
|||
|
top: 0
|
|||
|
});
|
|||
|
curState = 'fixed';
|
|||
|
}
|
|||
|
}
|
|||
|
function bottom() {
|
|||
|
if (curState !== 'bottom') {
|
|||
|
$root.removeClass('fixed').css({
|
|||
|
left: 0,
|
|||
|
top: rootBottomTop + 'px'
|
|||
|
});
|
|||
|
curState = 'bottom';
|
|||
|
}
|
|||
|
}
|
|||
|
function setState() {
|
|||
|
var scrollTop = $scrollTarget.scrollTop();
|
|||
|
if (scrollTop >= rootTop && scrollTop <= scrollBottom) {
|
|||
|
fixed();
|
|||
|
} else if (scrollTop < rootTop) {
|
|||
|
top();
|
|||
|
} else {
|
|||
|
bottom();
|
|||
|
}
|
|||
|
}
|
|||
|
function init() {
|
|||
|
if(!hasInit) {
|
|||
|
var interval, timeout;
|
|||
|
calc(true); setState();
|
|||
|
// run calc every 100 millisecond
|
|||
|
interval = setInterval(function() {
|
|||
|
calc();
|
|||
|
}, 100);
|
|||
|
timeout = setTimeout(function() {
|
|||
|
clearInterval(interval);
|
|||
|
}, 45000);
|
|||
|
window.pageLoad.then(function() {
|
|||
|
setTimeout(function() {
|
|||
|
clearInterval(interval);
|
|||
|
clearTimeout(timeout);
|
|||
|
}, 3000);
|
|||
|
});
|
|||
|
$scrollTarget.on('scroll', function() {
|
|||
|
disabled || setState();
|
|||
|
});
|
|||
|
$window.on('resize', function() {
|
|||
|
disabled || (calc(true), setState());
|
|||
|
});
|
|||
|
hasInit = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
setOptions(options);
|
|||
|
if (!disabled) {
|
|||
|
init();
|
|||
|
}
|
|||
|
$window.on('resize', window.throttle(function() {
|
|||
|
init();
|
|||
|
}, 200));
|
|||
|
return {
|
|||
|
setOptions: setOptions,
|
|||
|
refresh: function() {
|
|||
|
calc(true, { animation: false }); setState();
|
|||
|
}
|
|||
|
};
|
|||
|
}
|
|||
|
$.fn.affix = affix;
|
|||
|
});
|
|||
|
})();
|
|||
|
(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
function toc(options) {
|
|||
|
var $root = this, $window = $(window), $scrollTarget, $scroller, $tocUl = $('<ul class="toc toc--ellipsis"></ul>'), $tocLi, $headings, $activeLast, $activeCur,
|
|||
|
selectors = 'h1,h2,h3', container = 'body', scrollTarget = window, scroller = 'html, body', disabled = false,
|
|||
|
headingsPos, scrolling = false, hasRendered = false, hasInit = false;
|
|||
|
|
|||
|
function setOptions(options) {
|
|||
|
var _options = options || {};
|
|||
|
_options.selectors && (selectors = _options.selectors);
|
|||
|
_options.container && (container = _options.container);
|
|||
|
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
|
|||
|
_options.scroller && (scroller = _options.scroller);
|
|||
|
_options.disabled !== undefined && (disabled = _options.disabled);
|
|||
|
$headings = $(container).find(selectors).filter('[id]');
|
|||
|
$scrollTarget = $(scrollTarget);
|
|||
|
$scroller = $(scroller);
|
|||
|
}
|
|||
|
function calc() {
|
|||
|
headingsPos = [];
|
|||
|
$headings.each(function() {
|
|||
|
headingsPos.push(Math.floor($(this).position().top));
|
|||
|
});
|
|||
|
}
|
|||
|
function setState(element, disabled) {
|
|||
|
var scrollTop = $scrollTarget.scrollTop(), i;
|
|||
|
if (disabled || !headingsPos || headingsPos.length < 1) { return; }
|
|||
|
if (element) {
|
|||
|
$activeCur = element;
|
|||
|
} else {
|
|||
|
for (i = 0; i < headingsPos.length; i++) {
|
|||
|
if (scrollTop >= headingsPos[i]) {
|
|||
|
$activeCur = $tocLi.eq(i);
|
|||
|
} else {
|
|||
|
$activeCur || ($activeCur = $tocLi.eq(i));
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
$activeLast && $activeLast.removeClass('active');
|
|||
|
($activeLast = $activeCur).addClass('active');
|
|||
|
}
|
|||
|
function render() {
|
|||
|
if(!hasRendered) {
|
|||
|
$root.append($tocUl);
|
|||
|
$headings.each(function() {
|
|||
|
var $this = $(this);
|
|||
|
$tocUl.append($('<li></li>').addClass('toc-' + $this.prop('tagName').toLowerCase())
|
|||
|
.append($('<a></a>').text($this.text()).attr('href', '#' + $this.prop('id'))));
|
|||
|
});
|
|||
|
$tocLi = $tocUl.children('li');
|
|||
|
$tocUl.on('click', 'a', function(e) {
|
|||
|
e.preventDefault();
|
|||
|
var $this = $(this);
|
|||
|
scrolling = true;
|
|||
|
setState($this.parent());
|
|||
|
$scroller.scrollToAnchor($this.attr('href'), 400, function() {
|
|||
|
scrolling = false;
|
|||
|
});
|
|||
|
});
|
|||
|
}
|
|||
|
hasRendered = true;
|
|||
|
}
|
|||
|
function init() {
|
|||
|
var interval, timeout;
|
|||
|
if(!hasInit) {
|
|||
|
render(); calc(); setState(null, scrolling);
|
|||
|
// run calc every 100 millisecond
|
|||
|
interval = setInterval(function() {
|
|||
|
calc();
|
|||
|
}, 100);
|
|||
|
timeout = setTimeout(function() {
|
|||
|
clearInterval(interval);
|
|||
|
}, 45000);
|
|||
|
window.pageLoad.then(function() {
|
|||
|
setTimeout(function() {
|
|||
|
clearInterval(interval);
|
|||
|
clearTimeout(timeout);
|
|||
|
}, 3000);
|
|||
|
});
|
|||
|
$scrollTarget.on('scroll', function() {
|
|||
|
disabled || setState(null, scrolling);
|
|||
|
});
|
|||
|
$window.on('resize', window.throttle(function() {
|
|||
|
if (!disabled) {
|
|||
|
render(); calc(); setState(null, scrolling);
|
|||
|
}
|
|||
|
}, 100));
|
|||
|
}
|
|||
|
hasInit = true;
|
|||
|
}
|
|||
|
|
|||
|
setOptions(options);
|
|||
|
if (!disabled) {
|
|||
|
init();
|
|||
|
}
|
|||
|
$window.on('resize', window.throttle(function() {
|
|||
|
init();
|
|||
|
}, 200));
|
|||
|
return {
|
|||
|
setOptions: setOptions
|
|||
|
};
|
|||
|
}
|
|||
|
$.fn.toc = toc;
|
|||
|
});
|
|||
|
})();
|
|||
|
/*(function () {
|
|||
|
|
|||
|
})();*/
|
|||
|
</script><script>
|
|||
|
/* toc must before affix, since affix need to konw toc' height. */(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
var TOC_SELECTOR = window.TEXT_VARIABLES.site.toc.selectors;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
var $window = $(window);
|
|||
|
var $articleContent = $('.js-article-content');
|
|||
|
var $tocRoot = $('.js-toc-root'), $col2 = $('.js-col-aside');
|
|||
|
var toc;
|
|||
|
var tocDisabled = false;
|
|||
|
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
|
|||
|
var hasToc = $articleContent.find(TOC_SELECTOR).length > 0;
|
|||
|
|
|||
|
function disabled() {
|
|||
|
return $col2.css('display') === 'none' || !hasToc;
|
|||
|
}
|
|||
|
|
|||
|
tocDisabled = disabled();
|
|||
|
|
|||
|
toc = $tocRoot.toc({
|
|||
|
selectors: TOC_SELECTOR,
|
|||
|
container: $articleContent,
|
|||
|
scrollTarget: hasSidebar ? '.js-page-main' : null,
|
|||
|
scroller: hasSidebar ? '.js-page-main' : null,
|
|||
|
disabled: tocDisabled
|
|||
|
});
|
|||
|
|
|||
|
$window.on('resize', window.throttle(function() {
|
|||
|
tocDisabled = disabled();
|
|||
|
toc && toc.setOptions({
|
|||
|
disabled: tocDisabled
|
|||
|
});
|
|||
|
}, 100));
|
|||
|
|
|||
|
});
|
|||
|
})();
|
|||
|
(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
var $window = $(window), $pageFooter = $('.js-page-footer');
|
|||
|
var $pageAside = $('.js-page-aside');
|
|||
|
var affix;
|
|||
|
var tocDisabled = false;
|
|||
|
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
|
|||
|
|
|||
|
affix = $pageAside.affix({
|
|||
|
offsetBottom: $pageFooter.outerHeight(),
|
|||
|
scrollTarget: hasSidebar ? '.js-page-main' : null,
|
|||
|
scroller: hasSidebar ? '.js-page-main' : null,
|
|||
|
scroll: hasSidebar ? $('.js-page-main').children() : null,
|
|||
|
disabled: tocDisabled
|
|||
|
});
|
|||
|
|
|||
|
$window.on('resize', window.throttle(function() {
|
|||
|
affix && affix.setOptions({
|
|||
|
disabled: tocDisabled
|
|||
|
});
|
|||
|
}, 100));
|
|||
|
|
|||
|
window.pageAsideAffix = affix;
|
|||
|
});
|
|||
|
})();
|
|||
|
</script><!---->
|
|||
|
|
|||
|
</div>
|
|||
|
<script>(function () {
|
|||
|
var $root = document.getElementsByClassName('root')[0];
|
|||
|
if (window.hasEvent('touchstart')) {
|
|||
|
$root.dataset.isTouch = true;
|
|||
|
document.addEventListener('touchstart', function(){}, false);
|
|||
|
}
|
|||
|
})();
|
|||
|
</script>
|
|||
|
</body>
|
|||
|
</html>
|
|||
|
|