2891 lines
230 KiB
HTML
2891 lines
230 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>Alpine Linux dans un environnement virtuel KVM Lenovo - YannStatic</title>
|
|||
|
|
|||
|
<meta name="description" content="Alpine Linux est une distribution Linux ultra-légère, orientée sécurité et basée sur Musl (en) et BusyBox, principalement conçue pour un « utilisateur intens...">
|
|||
|
<link rel="canonical" href="https://static.rnmkcy.eu/2023/12/22/Alpine-Linux.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;">Alpine Linux dans un environnement virtuel KVM Lenovo</h1></header></div><meta itemprop="headline" content="Alpine Linux dans un environnement virtuel KVM Lenovo"><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">22 déc. 2023</span>
|
|||
|
|
|||
|
<span title="Modification" style="color:#00FF7F">20 mai 2024</span></li></ul></div><meta itemprop="datePublished" content="2024-05-20T00:00:00+02: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><em>Alpine Linux est une distribution Linux ultra-légère, orientée sécurité et basée sur Musl (en) et BusyBox, principalement conçue pour un « utilisateur intensif qui apprécie la sécurité, la simplicité et l’efficacité des ressources ». Elle utilise les patches PaX et Grsecurity du noyau par défaut et compile tous les binaires de l’espace utilisateur et exécutables indépendants de la position avec protection de destruction de la pile (<a href="https://fr.wikipedia.org/wiki/Alpine_Linux">Alpine Linux (wikipédia)</a>)</em></p>
|
|||
|
|
|||
|
<p><img src="/images/alpine-linux-logo.png" alt="" width="300" /></p>
|
|||
|
|
|||
|
<h2 id="alpine-linux">Alpine Linux</h2>
|
|||
|
|
|||
|
<p><em>Création machine virtuelle Alpine de type KVM avec 2 Go de RAM, 1 cœur de processeur et 8 Go de disque dur.</em></p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><a href="/2023/12/17/Installer_KVM_Kernel_Virtual_Machine_sur_un_serveur.html#création-machines-virtuelles-kvm-avec-la-commande-virsh">Création machines virtuelles KVM avec la commande virsh</a></li>
|
|||
|
<li><a href="/2023/12/17/Installer_KVM_Kernel_Virtual_Machine_sur_un_serveur.html#accéder-aux-machines-virtuelles-kvm-via-le-client-vnc">Accéder aux machines virtuelles KVM via le client VNC</a></li>
|
|||
|
<li><a href="https://wiki.alpinelinux.org/wiki/">Wiki Alpine Linux</a></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h3 id="créer-machine-virtuelle-alpineouest">Créer machine virtuelle AlpineOuest</h3>
|
|||
|
|
|||
|
<p>Création d’une image virtuelle AlpineOuest sous le serveur Lenovo rnmkcy.eu<br />
|
|||
|
On se connecte sur le serveur Lenovo en SSH, puis on exécute la commande suivante pour créer une machine virtuelle Alpine avec 2 Go de RAM, 1 cœur de processeur et 8 Go de disque dur</p>
|
|||
|
|
|||
|
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>virt-install <span class="nt">--os-variant</span><span class="o">=</span><span class="s2">"alpinelinux3.17"</span> <span class="se">\</span>
|
|||
|
<span class="nt">--name</span> AlpineOuest <span class="se">\</span>
|
|||
|
<span class="nt">--ram</span><span class="o">=</span>2048 <span class="se">\</span>
|
|||
|
<span class="nt">--vcpus</span><span class="o">=</span>1 <span class="se">\</span>
|
|||
|
<span class="nt">--cpu</span> host <span class="se">\</span>
|
|||
|
<span class="nt">--hvm</span> <span class="nt">--disk</span> <span class="nv">path</span><span class="o">=</span>/srv/kvm/libvirt/images/alpine-ouest,size<span class="o">=</span>8 <span class="se">\</span>
|
|||
|
<span class="nt">--cdrom</span> /srv/kvm/libvirt/boot/alpine-standard-3.19.0-x86_64.iso <span class="se">\</span>
|
|||
|
<span class="nt">--network</span> <span class="nv">bridge</span><span class="o">=</span>br0 <span class="se">\</span>
|
|||
|
<span class="nt">--graphics</span> vnc
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Le serveur Lenovo n’a pas d’affichage, il faut créer un tunnel ssh depuis un poste local</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh -L 5900:127.0.0.1:5900 leno@192.168.0.215 -p 55215 -i /home/yann/.ssh/lenovo-ed25519
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Puis lancer de ce même poste un client VNC : <code class="language-plaintext highlighter-rouge">localhost:9500</code> , la console s’affiche <br />
|
|||
|
<img src="/images/alpine-ouest01.png" alt="" /></p>
|
|||
|
|
|||
|
<h3 id="installer-et-configurer-alpineouest">Installer et configurer AlpineOuest</h3>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><a href="https://www.librebyte.net/en/tag/alpine/">Alpine tutos</a></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Une fois l’image ISO lancée, on arrive à un invite de connexion. <br />
|
|||
|
Indiquez <code class="language-plaintext highlighter-rouge">root</code> comme nom d’utilisateur, aucun mot de passe ne vous sera demandé à cette étape. <br />
|
|||
|
Le système est utilisable, mais on veut l’installer, ce qui passe par la commande suivante (clavier qwerty)</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setup-alpine
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Une suite de questions :</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>la langue du clavier (fr) keyboard layout (fr)</li>
|
|||
|
<li>le nom que vous voulez donner à la machine sur le réseau (ouestline.xyz)</li>
|
|||
|
<li>interface (eth0) et réseau</li>
|
|||
|
<li>mot de passe root (toor)</li>
|
|||
|
<li>votre fuseau horaire (Europe/paris)</li>
|
|||
|
<li>proxy none</li>
|
|||
|
<li>APK mirror (f)</li>
|
|||
|
<li>User à créer (aluser) et son mot de passe (aluser49)</li>
|
|||
|
<li>ssh key (none)</li>
|
|||
|
<li>ssh server (openssh)</li>
|
|||
|
<li>Disk & Install (vda)</li>
|
|||
|
<li>Utiliser (sys)</li>
|
|||
|
<li>Erase the disk vda (y)</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Normalement, vous n’avez rien à faire, les paramètres par défaut doivent convenir. Mais si vous le désirez, vous pouvez les modifier pour utiliser une interface particulière, une IP fixe, un serveur proxy, etc.</p>
|
|||
|
|
|||
|
<p>Une soixantaine de serveurs mirroir vous seront proposés pour télécharger les paquets. Choisissez un numéro dans la liste ou demandez au système de les tester et de sélectionner le plus rapide. Vous pouvez aussi modifier le fichier des sources. Il vous faudra ensuite choisir votre serveur SSH : OpenSSH, Dropbear ou aucun.</p>
|
|||
|
|
|||
|
<p>On termine par la méthode d’installation. Il en existe quatre :</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>none : le système et ses données sont placés en RAM et seront perdus après le redémarrage</li>
|
|||
|
<li>sys : le système et ses données sont placés sur un HDD/SSD</li>
|
|||
|
<li>data : le système est placé en RAM, les données sur un HDD/SSD</li>
|
|||
|
<li>lvm : utilisation de Logical Volume Manager, les deux choix précédents seront proposés (lvmsys, lvmdata)</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Si vous stockez le système en mémoire, il faudra trouver un moyen de sauvegarder la configuration. Vous pourrez le faire uniquement depuis un lecteur de disquettes (!) ou une clé USB. Une fois le système installé, vous pourrez l’utiliser directement s’il est placé en mémoire ou redémarrer si vous avez opté pour un stockage classique.</p>
|
|||
|
|
|||
|
<p>Il n’est pas conseillé d’utiliser directement le compte root pour les actions du quotidien.<br />
|
|||
|
Si utilisateur non créé dans la procédure d’installation, le créer avec son propre espace dans /home/</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>adduser aluser
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vous pouvez utiliser l’utilisateur pour vous connecter via SSH (impossible avec le compte root)<br />
|
|||
|
Relever l’adresse ip allouée : <code class="language-plaintext highlighter-rouge">ip a</code> –> 192.168.0.5</p>
|
|||
|
|
|||
|
<p>Sur un poste linux du réseau</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh aluser@192.168.0.5
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Une fois connecté ,vous pouvez accéder au “root” de manière classique avec la commande :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>su -
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Mise à jour</p>
|
|||
|
|
|||
|
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apk update
|
|||
|
apk upgrade
|
|||
|
<span class="c"># Vous pouvez fusionner les deux lignes avec </span>
|
|||
|
apk <span class="nt">-U</span> upgrade
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Editeur nano (Vous pouvez aussi opter pour vi qui est nativement présent sur le système)</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apk add nano
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="réseau">Réseau</h3>
|
|||
|
|
|||
|
<p><a href="https://www.cyberciti.biz/faq/how-to-configure-static-ip-address-on-alpine-linux/">How to configure static IP address on Alpine Linux</a></p>
|
|||
|
|
|||
|
<h4 id="activer-ipv6">Activer IPv6</h4>
|
|||
|
|
|||
|
<p>Si vous utilisez IPv6, faites ce qui suit pour activer IPv6 maintenant et à chaque démarrage :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>modprobe ipv6
|
|||
|
echo "ipv6" >> /etc/modules
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h4 id="configurer-interface">Configurer interface</h4>
|
|||
|
|
|||
|
<p>Configuration de loopback (obligatoire)</p>
|
|||
|
|
|||
|
<p class="info">Remarque : La configuration loopback doit apparaître en premier dans /etc/network/interfaces pour éviter les problèmes de réseau.</p>
|
|||
|
|
|||
|
<p>Pour configurer loopback, ajoutez ce qui suit au fichier <code class="language-plaintext highlighter-rouge">/etc/network/interfaces</code></p>
|
|||
|
|
|||
|
<p>Contenu de <code class="language-plaintext highlighter-rouge">/etc/network/interfaces</code></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>...
|
|||
|
auto lo
|
|||
|
iface lo inet loopback
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p class="info">Ce qui précède fonctionne pour configurer l’adresse IPv4 loopback (127.0.0.1) et l’adresse IPv6 loopback (::1) — si vous avez activé IPv6.</p>
|
|||
|
|
|||
|
<p>Le fichier finale de configuration <code class="language-plaintext highlighter-rouge">/etc/network/interfaces</code></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/etc/network/interfaces
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>auto lo
|
|||
|
iface lo inet loopback
|
|||
|
|
|||
|
auto eth0
|
|||
|
iface eth0 inet static
|
|||
|
address 192.168.0.216/24
|
|||
|
gateway 192.168.0.254
|
|||
|
<span class="nb">hostname </span>linux-alpine
|
|||
|
|
|||
|
iface eth0 inet static
|
|||
|
address 2a01:e0a:9c8:2083::1/64
|
|||
|
gateway fe80::8e97:eaff:fe39:66d6
|
|||
|
pre-up <span class="nb">echo </span>0 <span class="o">></span> /proc/sys/net/ipv6/conf/eth0/accept_ra
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Fichier de résolution dns</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/etc/resolv.conf
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nameserver 1.1.1.1
|
|||
|
nameserver 192.168.0.254
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p class="warning">ATTENTION , pour l’adresse ipv6 statique il faut renseigner le NextHop de la box free avec l’adresse inet6 <code class="language-plaintext highlighter-rouge">fe80::5054:ff:fe7c:b46c</code> de l’interface réseau eth0<br />
|
|||
|
<img src="/images/alpine-ouest02.png" alt="" /></p>
|
|||
|
|
|||
|
<p>Les modifications apportées à /etc/network/interfaces peuvent être activées en exécutant</p>
|
|||
|
|
|||
|
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rc-service networking restart <span class="c"># par défaut</span>
|
|||
|
/etc/init.d/networking restart <span class="c"># alternative</span>
|
|||
|
service networking restart <span class="c"># autre alternative</span>
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="hostname">hostname</h3>
|
|||
|
|
|||
|
<p>Vérifier hostname</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hostname
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Renvoie <code class="language-plaintext highlighter-rouge">ouestline.xyz</code></p>
|
|||
|
|
|||
|
<p>Si l’on veut changer de hostname</p>
|
|||
|
|
|||
|
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"linux-alpine"</span> <span class="o">></span> /etc/hostname
|
|||
|
</code></pre></div></div>
|
|||
|
<p>Activez immédiatement la modification en exécutant la commande suivante.</p>
|
|||
|
|
|||
|
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">hostname</span> <span class="nt">-F</span> /etc/hostname
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="php">PHP</h3>
|
|||
|
|
|||
|
<p><a href="https://www.librebyte.net/en/systems-deployment/how-to-install-php-php-fpm-in-alpine-linux/">How to install PHP, PHP-FPM in Alpine Linux?</a></p>
|
|||
|
|
|||
|
<h4 id="installer-php81">Installer php81</h4>
|
|||
|
|
|||
|
<p>Vérifier les dépôts de mon installation <code class="language-plaintext highlighter-rouge">cat /etc/apk/repositories</code>
|
|||
|
Vérifiez que le dépôt communautaire est actif.</p>
|
|||
|
|
|||
|
<p>Mise à jour</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apk update && apk upgrade
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Liste des paquets php</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apk search ph8 | more
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[...]
|
|||
|
php81-8.1.27-r0
|
|||
|
php81-fpm-8.1.27-r0
|
|||
|
[...]
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>PHP-FPM introduit le concept de pools, chaque pool peut recevoir des connexions sur une socket PTC/IP (IP:Port) ou UNIX, et peut fonctionner sous un utilisateur et un groupe différents. Chaque pool a son fichier de configuration.</p>
|
|||
|
|
|||
|
<p>Installation</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apk add php81 php81-fpm
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Résultat</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(1/5) Installing php81-common (8.1.27-r0)
|
|||
|
(2/5) Installing pcre2 (10.42-r2)
|
|||
|
(3/5) Installing libxml2 (2.11.6-r0)
|
|||
|
(4/5) Installing php81 (8.1.27-r0)
|
|||
|
(5/5) Installing php81-fpm (8.1.27-r0)
|
|||
|
Executing busybox-1.36.1-r15.trigger
|
|||
|
OK: 438 MiB in 118 packages
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Une fois le processus terminé, vous pouvez explorer les paramètres par défaut, voici un exemple de la structure du répertoire:</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tree /etc/php81/
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><img src="/images/alpine-ouest16.png" alt="" width="400" /></p>
|
|||
|
|
|||
|
<p>Vous pouvez découvrir la valeur de la directive écoute en exécutant la commande</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grep 'listen =' -R /etc/php81/
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/etc/php81/php-fpm.d/www.conf:listen = 127.0.0.1:9000
|
|||
|
/etc/php81/php-fpm.d/www.conf:;pm.status_listen = 127.0.0.1:9001
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>La seconde ligne est commentée, l’écoute se fait sur le port 9000</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>listen = 127.0.0.1:9000
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Créer un lien symbolique vers la version correspondante</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ln -s /usr/bin/php81 /usr/bin/php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Afficher les versions installées de php</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>php -v
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PHP 8.1.27 (cli) (built: Dec 22 2023 21:16:31) (NTS)
|
|||
|
Copyright (c) The PHP Group
|
|||
|
Zend Engine v4.1.27, Copyright (c) Zend Technologies
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h4 id="gérer-le-service-php-fpm81">Gérer le service php-fpm81</h4>
|
|||
|
|
|||
|
<p>Démarrer, arrêter ou redémarrer le service php-fpm8, en utilisant le système d’initialisation OpenRC qui est le système init par défaut dans les distributions comme Gentoo et Alpine Linux.</p>
|
|||
|
|
|||
|
<p>Vérifier l’état : <code class="language-plaintext highlighter-rouge">rc-service php-fpm81 status</code> → <code class="language-plaintext highlighter-rouge">* status: stopped</code><br />
|
|||
|
Démarrer le service : <code class="language-plaintext highlighter-rouge">rc-service php-fpm81 start</code></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> * Caching service dependencies ... [ ok ]
|
|||
|
* Checking /etc/php81/php-fpm.conf ...
|
|||
|
* /run/php-fpm81: creating directory
|
|||
|
* Starting PHP FastCGI Process Manager ... [ ok ]
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Maintenant, vous pouvez vérifier si le service a démarré avec succès: <code class="language-plaintext highlighter-rouge">rc-service php-fpm81 status</code> → <code class="language-plaintext highlighter-rouge">* status: started</code></p>
|
|||
|
|
|||
|
<p>Lancement automatiquement après un redémarrage du système</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rc-update add php-fpm81 default
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><code class="language-plaintext highlighter-rouge">* service php-fpm81 added to runlevel default</code></p>
|
|||
|
|
|||
|
<h4 id="vérifier-et-recharger-php-fpm81">Vérifier et Recharger php-fpm81</h4>
|
|||
|
|
|||
|
<p>Chaque fois que vous modifiez les fichiers de configuration, vérifiez d’abord les modifications avec:</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>php-fpm81 -t
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><code class="language-plaintext highlighter-rouge">[02-Jan-2024 14:33:00] NOTICE: configuration file /etc/php81/php-fpm.conf test is successful</code><br />
|
|||
|
puis rechargez les configurations:</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rc-service php-fpm81 reload
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><code class="language-plaintext highlighter-rouge">* Reloading PHP FastCGI Process Manager ... [ ok ]</code></p>
|
|||
|
|
|||
|
<p>redémarrer et arrêter</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rc-service php-fpm81 restart
|
|||
|
rc-service php-fpm81 stop
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="caddy-server">Caddy server</h3>
|
|||
|
|
|||
|
<h4 id="installer-et-configurer">Installer et configurer</h4>
|
|||
|
|
|||
|
<p>Installer caddy</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apk update
|
|||
|
apk add caddy
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>J’ai une erreur , il ne trouve pas le paquet caddy</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ERROR: unable to select packages:
|
|||
|
caddy (no such package):
|
|||
|
required by: world[caddy]
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Voir le site : Alpine Linux packages https://pkgs.alpinelinux.org/packages<br />
|
|||
|
<img src="/images/alpine-ouest03.png" alt="" /><br />
|
|||
|
caddy est dans le dépôt community</p>
|
|||
|
|
|||
|
<p>Vérifier les dépôts de mon installation <code class="language-plaintext highlighter-rouge">cat /etc/apk/repositories</code></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#/media/cdrom/apks
|
|||
|
http://mirrors.ircam.fr/pub/alpine/v3.19/main
|
|||
|
#http://mirrors.ircam.fr/pub/alpine/v3.19/community
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Il faut décommenter le dépôt community, exécuter <code class="language-plaintext highlighter-rouge">apk update</code> puis installer caddy <code class="language-plaintext highlighter-rouge">apk add caddy</code></p>
|
|||
|
|
|||
|
<p><strong>DNS OVH</strong> du domaine et sous-domaines ouestline.xyz pointent sur l’adresse ipv6 <code class="language-plaintext highlighter-rouge">2a01:e0a:9c8:2083::1/64</code> de la machine alpine</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$TTL 3600
|
|||
|
@ IN SOA dns111.ovh.net. tech.ovh.net. (2024020607 86400 3600 3600000 300)
|
|||
|
IN NS ns111.ovh.net.
|
|||
|
IN NS dns111.ovh.net.
|
|||
|
IN AAAA 2a01:e0a:9c8:2083::1
|
|||
|
dev IN AAAA 2a01:e0a:9c8:2083::1
|
|||
|
dice IN AAAA 2a01:e0a:9c8:2083::1
|
|||
|
osm IN AAAA 2a01:e0a:9c8:2083::1
|
|||
|
static IN AAAA 2a01:e0a:9c8:2083::1
|
|||
|
tst IN AAAA 2a01:e0a:9c8:2083::1
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Créer une configuration de test dans le fichier <code class="language-plaintext highlighter-rouge">/etc/caddy/Caddyfile</code></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Caddy's configuration file
|
|||
|
# see: https://caddyserver.com/docs/caddyfile
|
|||
|
ouestline.xyz
|
|||
|
|
|||
|
respond "Hello, privacy!"
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Lancer le service caddy</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>service caddy start
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> * Caching service dependencies ... [ ok ]
|
|||
|
* Starting Caddy web server ... [ ok ]
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Puis tester sur le lien https://ouestline.xyz<br />
|
|||
|
<img src="/images/alpine-ouest04.png" alt="" /></p>
|
|||
|
|
|||
|
<p>Tester une autre configuration<br />
|
|||
|
On va créer un fichier <code class="language-plaintext highlighter-rouge">/var/lib/caddy/index.html</code></p>
|
|||
|
|
|||
|
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><!DOCTYPE html></span>
|
|||
|
<span class="nt"><html></span>
|
|||
|
<span class="nt"><head></span>
|
|||
|
<span class="nt"><title></span>Hello from Caddy!<span class="nt"></title></span>
|
|||
|
<span class="nt"></head></span>
|
|||
|
<span class="nt"><body></span>
|
|||
|
<span class="nt"><h1</span> <span class="na">style=</span><span class="s">"font-family: sans-serif"</span><span class="nt">></span>This page is being served via Caddy<span class="nt"></h1></span>
|
|||
|
<span class="nt"></body></span>
|
|||
|
<span class="nt"></html></span>
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Modifier le fichier de configuration <code class="language-plaintext highlighter-rouge">/etc/caddy/Caddyfile</code></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ouestline.xyz {
|
|||
|
root * /var/lib/caddy
|
|||
|
file_server
|
|||
|
}
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Il s’agit d’une configuration de base de Caddy, qui déclare que le domaine ouestline.xyz de votre serveur doit être servi avec des fichiers provenant de <code class="language-plaintext highlighter-rouge">/var/lib/caddy</code></p>
|
|||
|
|
|||
|
<p>On lance le service : <code class="language-plaintext highlighter-rouge">service caddy start</code> puis ouvre le lien https://ouestline.xyz<br />
|
|||
|
<img src="/images/alpine-ouest05.png" alt="" width="400" /></p>
|
|||
|
|
|||
|
<p>Pour un lancement au démarrage de la machine</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rc-update add caddy
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h4 id="prise-en-charge-php">Prise en charge PHP</h4>
|
|||
|
|
|||
|
<p><em>Prérequis, PHP est installé avec php-fpm</em></p>
|
|||
|
|
|||
|
<p>Installer gzip et zstd</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apk add gzip zstd
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>fichier de configuration <code class="language-plaintext highlighter-rouge">/etc/caddy/Caddyfile</code></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>domain.tld {
|
|||
|
root * /chemin/racine
|
|||
|
encode gzip zstd
|
|||
|
php_fastcgi 127.0.0.1:9000
|
|||
|
file_server {
|
|||
|
index index.html index.php;
|
|||
|
}
|
|||
|
}
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Recharger</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>caddy fmt --overwrite /etc/caddy/Caddyfile
|
|||
|
service caddy reload
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h4 id="sites-https">Sites HTTPS</h4>
|
|||
|
|
|||
|
<p><strong>Sites : ouestline.xyz et sous-domaines static, osm, dice, tst et dev</strong></p>
|
|||
|
|
|||
|
<p>Modifier le fichier de configuration <code class="language-plaintext highlighter-rouge">/etc/caddy/Caddyfile</code></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ouestline.xyz {
|
|||
|
root * /mnt/nfs-share/alpine/default
|
|||
|
php_fastcgi 127.0.0.1:9000
|
|||
|
file_server {
|
|||
|
index index.html index.php
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static.ouestline.xyz {
|
|||
|
root * /mnt/nfs-share/multimedia/Divers/static
|
|||
|
file_server
|
|||
|
}
|
|||
|
|
|||
|
osm.ouestline.xyz {
|
|||
|
root * /mnt/nfs-share/multimedia/Divers/osm-new
|
|||
|
file_server
|
|||
|
}
|
|||
|
|
|||
|
dice.ouestline.xyz {
|
|||
|
root * /mnt/nfs-share/multimedia/Divers/diceware
|
|||
|
file_server
|
|||
|
}
|
|||
|
|
|||
|
tst.ouestline.xyz {
|
|||
|
root * /mnt/nfs-share/alpine/tst
|
|||
|
file_server
|
|||
|
}
|
|||
|
|
|||
|
dev.ouestline.xyz {
|
|||
|
root * /mnt/nfs-share/alpine/dev
|
|||
|
encode gzip zstd
|
|||
|
php_fastcgi 127.0.0.1:9000
|
|||
|
file_server
|
|||
|
}
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Reformater le fichier</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>caddy fmt --overwrite /etc/caddy/Caddyfile
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Recharger le service caddy</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>service caddy reload
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="samba-client-inactif">Samba client (INACTIF)</h3>
|
|||
|
|
|||
|
<p>Installer samba client</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apk add samba-client cifs-utils
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Créer le dossier de montage</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir /mnt/nfs-share/
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Le fichier d’autorisation <code class="language-plaintext highlighter-rouge">/root/.smb-credentials</code></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>username=aluser
|
|||
|
password=xxxxxxxxxxxxxxx
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Tester le montage</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mount -t cifs //192.168.0.215/thinkshare /mnt/nfs-share -o rw,credentials=/root/.smb-credentials,uid=aluser,gid=aluser
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>On a bien les droits utilisateur</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/home/aluser # ls -la /mnt/nfs-share
|
|||
|
total 4
|
|||
|
drwxr-xr-x 2 aluser aluser 0 Dec 19 20:57 .
|
|||
|
drwxr-xr-x 3 aluser aluser 4096 Dec 19 20:56 ..
|
|||
|
-rwxr-xr-x 1 aluser aluser 0 Dec 19 20:57 test.txt
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Le fichier /etc/fstab</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//192.168.0.215/thinkshare /mnt/sambashare cifs rw,credentials=/root/.smb-credentials,uid=aluser,gid=aluser
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Activer le montage</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rc-update add netmount boot
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><strong>Désinstaller samba client</strong><br />
|
|||
|
Supprimer la ligne dans fstab<br />
|
|||
|
Supprimer les paquets : <code class="language-plaintext highlighter-rouge">sudo apk del samba-client cifs-utils</code></p>
|
|||
|
|
|||
|
<p>Désinstaller le mount au boot : <code class="language-plaintext highlighter-rouge">sudo rc-update del netmount boot</code></p>
|
|||
|
|
|||
|
<h3 id="nfs">NFS</h3>
|
|||
|
|
|||
|
<p>Installer le paquet nfs</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apk add nfs-utils
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Démarrage automatique</p>
|
|||
|
|
|||
|
<p>Exporter les répertoires dans /etc/exports, puis</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rc-update add nfs
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Si vous avez besoin de monter un partage nfs à partir du fichier fstab au démarrage du système</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rc-update add nfsmount
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>ou</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rc-update add netmount
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vous pouvez vérifier les services de démarrage :</p>
|
|||
|
|
|||
|
<p>rc-status</p>
|
|||
|
|
|||
|
<p>Démarrez maintenant</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rc-service nfs start
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>ou si vous avez besoin de monter un partage nfs à partir du fichier fstab maintenant</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rc-service nfsmount start
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>ou</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rc-service netmount start
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><strong>Montage du partage NFS disponible sur le réseau</strong></p>
|
|||
|
|
|||
|
<p>Création point de montage</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo mkdir -p /mnt/nfs-share
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajout du partage réseau à fstab</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/fstab
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ligne à ajouter</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>192.168.0.215:/ /mnt/nfs-share nfs4 soft,intr,rsize=8192,wsize=8192
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Lancer le service et vérifier</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo rc-service nfsmount start
|
|||
|
ls /mnt/nfs-share/
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>partage nfs à partir du fichier fstab au démarrage du système</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rc-update add nfsmount
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="parefeu-nftables">Parefeu nftables</h3>
|
|||
|
|
|||
|
<p><em>Parmi les avantages de nftables sur iptables, il y a moins de duplication de code et plus de débit. nftables est configuré via l’utilitaire espace utilisateur nft, tandis que netfilter est configuré via les utilitaires iptables, ip6tables, arptables et ebtables.</em></p>
|
|||
|
|
|||
|
<p>On installe le paquet nftables</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apk add nftables
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Par défaut les régles <strong>Basic IPv4/IPv6 stateful firewall for server/workstation.</strong> présentes dans le fichier <code class="language-plaintext highlighter-rouge">/etc/nftables.nft</code> sont chargés</p>
|
|||
|
|
|||
|
<p>Modifier le fichier</p>
|
|||
|
|
|||
|
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/usr/sbin/nft -f</span>
|
|||
|
<span class="c"># vim: set ts=4 sw=4:</span>
|
|||
|
<span class="c"># You can find examples in /usr/share/nftables/.</span>
|
|||
|
|
|||
|
<span class="c"># Clear all prior state</span>
|
|||
|
flush ruleset
|
|||
|
|
|||
|
<span class="c"># Basic IPv4/IPv6 stateful firewall for server/workstation.</span>
|
|||
|
table inet filter <span class="o">{</span>
|
|||
|
chain input <span class="o">{</span>
|
|||
|
<span class="nb">type </span>filter hook input priority 0<span class="p">;</span> policy drop<span class="p">;</span>
|
|||
|
|
|||
|
iifname lo accept <span class="se">\</span>
|
|||
|
comment <span class="s2">"Accept any localhost traffic"</span>
|
|||
|
|
|||
|
ct state <span class="o">{</span> established, related <span class="o">}</span> accept <span class="se">\</span>
|
|||
|
comment <span class="s2">"Accept traffic originated from us"</span>
|
|||
|
|
|||
|
tcp dport http
|
|||
|
tcp dport https
|
|||
|
|
|||
|
tcp dport 55216 accept <span class="se">\</span>
|
|||
|
comment <span class="s2">"Port SSH 55216"</span>
|
|||
|
|
|||
|
ct state invalid drop <span class="se">\</span>
|
|||
|
comment <span class="s2">"Drop invalid connections"</span>
|
|||
|
|
|||
|
tcp dport 113 reject with icmpx <span class="nb">type </span>port-unreachable <span class="se">\</span>
|
|||
|
comment <span class="s2">"Reject AUTH to make it fail fast"</span>
|
|||
|
|
|||
|
<span class="c"># ICMPv4</span>
|
|||
|
|
|||
|
ip protocol icmp icmp <span class="nb">type</span> <span class="o">{</span>
|
|||
|
echo-reply, <span class="c"># type 0</span>
|
|||
|
destination-unreachable, <span class="c"># type 3</span>
|
|||
|
echo-request, <span class="c"># type 8</span>
|
|||
|
time-exceeded, <span class="c"># type 11</span>
|
|||
|
parameter-problem, <span class="c"># type 12</span>
|
|||
|
<span class="o">}</span> accept <span class="se">\</span>
|
|||
|
comment <span class="s2">"Accept ICMP"</span>
|
|||
|
|
|||
|
<span class="c"># ICMPv6</span>
|
|||
|
|
|||
|
ip6 nexthdr icmpv6 icmpv6 <span class="nb">type</span> <span class="o">{</span>
|
|||
|
destination-unreachable, <span class="c"># type 1</span>
|
|||
|
packet-too-big, <span class="c"># type 2</span>
|
|||
|
time-exceeded, <span class="c"># type 3</span>
|
|||
|
parameter-problem, <span class="c"># type 4</span>
|
|||
|
echo-request, <span class="c"># type 128</span>
|
|||
|
echo-reply, <span class="c"># type 129</span>
|
|||
|
<span class="o">}</span> accept <span class="se">\</span>
|
|||
|
comment <span class="s2">"Accept basic IPv6 functionality"</span>
|
|||
|
|
|||
|
ip6 nexthdr icmpv6 icmpv6 <span class="nb">type</span> <span class="o">{</span>
|
|||
|
nd-router-solicit, <span class="c"># type 133</span>
|
|||
|
nd-router-advert, <span class="c"># type 134</span>
|
|||
|
nd-neighbor-solicit, <span class="c"># type 135</span>
|
|||
|
nd-neighbor-advert, <span class="c"># type 136</span>
|
|||
|
<span class="o">}</span> ip6 hoplimit 255 accept <span class="se">\</span>
|
|||
|
comment <span class="s2">"Allow IPv6 SLAAC"</span>
|
|||
|
|
|||
|
ip6 nexthdr icmpv6 icmpv6 <span class="nb">type</span> <span class="o">{</span>
|
|||
|
mld-listener-query, <span class="c"># type 130</span>
|
|||
|
mld-listener-report, <span class="c"># type 131</span>
|
|||
|
mld-listener-reduction, <span class="c"># type 132</span>
|
|||
|
mld2-listener-report, <span class="c"># type 143</span>
|
|||
|
<span class="o">}</span> ip6 saddr fe80::/10 accept <span class="se">\</span>
|
|||
|
comment <span class="s2">"Allow IPv6 multicast listener discovery on link-local"</span>
|
|||
|
|
|||
|
ip6 saddr fe80::/10 udp sport 547 udp dport 546 accept <span class="se">\</span>
|
|||
|
comment <span class="s2">"Accept DHCPv6 replies from IPv6 link-local addresses"</span>
|
|||
|
<span class="o">}</span>
|
|||
|
|
|||
|
chain forward <span class="o">{</span>
|
|||
|
<span class="nb">type </span>filter hook forward priority 0<span class="p">;</span> policy drop<span class="p">;</span>
|
|||
|
<span class="o">}</span>
|
|||
|
|
|||
|
chain output <span class="o">{</span>
|
|||
|
<span class="nb">type </span>filter hook output priority 0<span class="p">;</span> policy accept<span class="p">;</span>
|
|||
|
<span class="o">}</span>
|
|||
|
<span class="o">}</span>
|
|||
|
|
|||
|
<span class="c"># The state of stateful objects saved on the nftables service stop.</span>
|
|||
|
include <span class="s2">"/var/lib/nftables/*.nft"</span>
|
|||
|
|
|||
|
<span class="c"># Rules</span>
|
|||
|
include <span class="s2">"/etc/nftables.d/*.nft"</span>
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Relancer nftables</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rc-service nftables restart
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Vérifier les configurations : <code class="language-plaintext highlighter-rouge">nft list ruleset</code></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>table inet filter {
|
|||
|
chain input {
|
|||
|
type filter hook input priority filter; policy drop;
|
|||
|
iifname "lo" accept comment "Accept any localhost traffic"
|
|||
|
ct state { established, related } accept comment "Accept traffic originated from us"
|
|||
|
tcp dport { 80, 443 } accept
|
|||
|
tcp dport 55216 accept comment "Port SSH 55216"
|
|||
|
ct state invalid drop comment "Drop invalid connections"
|
|||
|
tcp dport 113 reject comment "Reject AUTH to make it fail fast"
|
|||
|
ip protocol icmp icmp type { echo-reply, destination-unreachable, echo-request, time-exceeded, parameter-problem } accept comment "Accept ICMP"
|
|||
|
ip6 nexthdr ipv6-icmp icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, echo-reply } accept comment "Accept basic IPv6 functionality"
|
|||
|
ip6 nexthdr ipv6-icmp ip6 hoplimit 255 icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept comment "Allow IPv6 SLAAC"
|
|||
|
ip6 nexthdr ipv6-icmp icmpv6 type { mld-listener-query, mld-listener-report, mld-listener-done, mld2-listener-report } ip6 saddr fe80::/10 accept comment "Allow IPv6 multicast listener discovery on link-local"
|
|||
|
ip6 saddr fe80::/10 udp sport 547 udp dport 546 accept comment "Accept DHCPv6 replies from IPv6 link-local addresses"
|
|||
|
}
|
|||
|
|
|||
|
chain forward {
|
|||
|
type filter hook forward priority filter; policy drop;
|
|||
|
}
|
|||
|
|
|||
|
chain output {
|
|||
|
type filter hook output priority filter; policy accept;
|
|||
|
}
|
|||
|
}
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>nftables exécution et chargement des configurations lorsque le système démarre.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rc-update add nftables
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><code class="language-plaintext highlighter-rouge">* service nftables added to runlevel default</code></p>
|
|||
|
|
|||
|
<h3 id="accès-administration-via-sudo">Accès administration via sudo</h3>
|
|||
|
|
|||
|
<p>Installer sudo</p>
|
|||
|
|
|||
|
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apk update
|
|||
|
apk add <span class="nb">sudo</span>
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ensuite, créons un nouvel utilisateur dans Alpine Linux et accordons des privilèges de sudo à l’utilisateur nouvellement créé.</p>
|
|||
|
|
|||
|
<p><strong>Créer un utilisateur de sudo</strong></p>
|
|||
|
|
|||
|
<p>Pour créer un nouvel utilisateur dans Alpine Linux, nous utilisons la commande adduser</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>adduser admi
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Création du nouvel utilisateur nommé “admi”.</p>
|
|||
|
|
|||
|
<p>Saisissez le mot de passe deux fois pour le nouvel utilisateur et remplissez la création de l’utilisateur.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Changing password for admi
|
|||
|
New password:
|
|||
|
Retype password:
|
|||
|
passwd: password for admi changed by root
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Nous venons de créer un nouvel utilisateur normal. L’utilisateur n’a pas encore de droits administratifs.</p>
|
|||
|
|
|||
|
<p>Vous pouvez vérifier si un utilisateur a des privilèges de sudo ou non dans Alpine Linux en utilisant la commande</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -lU admi
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Sortie :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>User admi is not allowed to run sudo on ouestline.
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajoutons le nouvel utilisateur à la liste des sudoers, afin qu’il puisse effectuer des opérations administratives</p>
|
|||
|
|
|||
|
<p><strong>Accorder des privilèges de sudo aux utilisateurs</strong></p>
|
|||
|
|
|||
|
<p>Nous pouvons le faire de deux façons.</p>
|
|||
|
|
|||
|
<p><u>Méthode 1</u></p>
|
|||
|
|
|||
|
<p>Pour attribuer des permissions de sudo à un utilisateur dans Alpine Linux, il suffit de l’ajouter au groupe <strong>wheel</strong> qui est un groupe spécial dans certains systèmes d’exploitation comme Unix. Tous les membres du groupe <strong>wheel</strong> peuvent effectuer des tâches administratives. Le groupe <strong>wheel</strong> est similaire au groupe de sudo dans les systèmes basés sur Debian.</p>
|
|||
|
|
|||
|
<p>Exécutez la commande suivante pour permettre aux membres du groupe <strong>wheel</strong> d’exécuter n’importe quelle commande</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>echo '%wheel ALL=(ALL) ALL' > /etc/sudoers.d/wheel
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Puis ajouter l’utilisateur “admi” au groupe wheel</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>adduser admi wheel
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Nous avons ajouté l’utilisateur admi à la liste des sudoers.
|
|||
|
Vérifiez si l’utilisateur a accès au sudo</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -lU admi
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><u>Méthode 2</u></p>
|
|||
|
|
|||
|
<p>Une autre façon d’attribuer les permissions de sudo à un utilisateur est de l’ajouter directement dans le fichier de configuration <code class="language-plaintext highlighter-rouge">/etc/sudoers</code></p>
|
|||
|
|
|||
|
<p>Pour donner des permissions de sudo à l’utilisateur “admi”, modifier le fichier “/etc/sudoers” :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>visudo
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajouter la ligne suivante:</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>admi ALL=(ALL) ALL
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><img src="/images/alpine-ouest14.png" alt="" /><br />
|
|||
|
Editeur vi → ESC puis <code class="language-plaintext highlighter-rouge">:wq</code></p>
|
|||
|
|
|||
|
<p>Pour donner des permissions de sudo à l’utilisateur “admi”, sans saisie mot de passe</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>admi ALL=(ALL) NOPASSWD: ALL
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><strong>Vérifiez si un utilisateur a accès au sudo</strong></p>
|
|||
|
|
|||
|
<p>Pour vérifier si un utilisateur a des droits sudo dans Alpine Linux, exécutez cette commande :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -lU admi
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Sortie :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>User admi may run the following commands on ouestline:
|
|||
|
(ALL) ALL
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Comme vous pouvez le voir, l’utilisateur “admi” peut exécuter toutes les commandes</p>
|
|||
|
|
|||
|
<p>Passons au nouvel utilisateur de sudo et vérifions s’il peut exécuter des tâches de sudo.<br />
|
|||
|
Pour passer au nouvel utilisateur, c’est-à-dire admi dans notre cas, exécutez :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>su - admi
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Exécutez toute opération de sudo pour vérifier si l’utilisateur a vraiment des permissions de sudo.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apk update
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><img src="/images/alpine-ouest15.png" alt="" /></p>
|
|||
|
|
|||
|
<p>Oui, l’utilisateur fait partie du groupe des utilisateurs administratifs.</p>
|
|||
|
|
|||
|
<p><strong>Supprimer les privilèges sudo à un utilisateur</strong></p>
|
|||
|
|
|||
|
<p>Pour révoquer les autorisations de sudo d’un utilisateur dans Alpine Linux, il suffit de le supprimer du groupe wheel en utilisant la commande <code class="language-plaintext highlighter-rouge">gpasswd</code>. La commande <code class="language-plaintext highlighter-rouge">gpasswd</code> n’est pas disponible dans l’image de base alpine. Vous devez installer le paquet <code class="language-plaintext highlighter-rouge">shadow</code> afin d’obtenir la commande <code class="language-plaintext highlighter-rouge">gpasswd</code>.</p>
|
|||
|
|
|||
|
<p>Pour installer le paquet shadow dans Alpine Linux, exécutez la commande suivante en tant qu’utilisateur root :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apk add shadow
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Maintenant, vous pouvez supprimer les privilèges de sudo d’un utilisateur, par exemple ostechnix, en utilisant la commande:</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpasswd -d admi wheel
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Sortie :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Removing user admi from group wheel
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>L’utilisateur admi a été retiré du groupe de roues. Vous pouvez le vérifier</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo -lU admi
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>L’utilisateur admi est maintenant devenu un utilisateur régulier. Il ne peut plus effectuer d’opérations de sudo.</p>
|
|||
|
|
|||
|
<p>Si vous ne voulez plus de cet utilisateur, retirez-le entièrement du système en utilisant cette commande</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>userdel -r admi
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ici, le drapeau <code class="language-plaintext highlighter-rouge">-r</code> est utilisé pour supprimer le répertoire <code class="language-plaintext highlighter-rouge">$HOME</code> de l’utilisateur.</p>
|
|||
|
|
|||
|
<h3 id="openssh-avec-clés">OpenSSH avec clés</h3>
|
|||
|
|
|||
|
<p><em>Connexion ssh sur un autre port avec un jeu de clés</em></p>
|
|||
|
|
|||
|
<p>Générer une paire de clé sur l’ordinateur de bureau PC1<br />
|
|||
|
Générer une paire de clé curve25519-sha256 (ECDH avec Curve25519 et SHA2) pour une liaison SSH avec le serveur.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh-keygen -t ed25519 -o -a 100 -f ~/.ssh/alpine-vm
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Copier la clé publique <code class="language-plaintext highlighter-rouge">alpine-vm.pub</code> dans le presse-papier</p>
|
|||
|
|
|||
|
<p>On se connecte sur la machine virtuelle alpine linux</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh aluser@192.168.0.216
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Créer le répertoire et ouvrir nouveau fichier</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir -p $HOME/.ssh/
|
|||
|
nano $HOME/.ssh/authorized_keys
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Coller le contenu du presse-papier , sauver le fichier et sortir</p>
|
|||
|
|
|||
|
<p>Passer en mode su</p>
|
|||
|
|
|||
|
<p>Modifier la configuration serveur SSH</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /etc/ssh/sshd_config
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Modifier</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Port = 55216
|
|||
|
PasswordAuthentication no
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Relancer le serveur</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>service sshd restart
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Modifier /etc/motd</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> _ _ _ ___ _
|
|||
|
/_\ | | _ __ (_) _ _ ___ / _ \ _ _ ___ ___| |_
|
|||
|
/ _ \ | || '_ \| || ' \ / -_)| (_) || || |/ -_)(_-<| _|
|
|||
|
/_/ \_\|_|| .__/|_||_||_|\___| \___/ \_,_|\___|/__/ \__|
|
|||
|
|_| _ _ _
|
|||
|
___ _ _ ___ ___| |_ | |(_) _ _ ___ __ __ _ _ ___
|
|||
|
/ _ \| || |/ -_)(_-<| _|| || || ' \ / -_) _ \ \ /| || ||_ /
|
|||
|
\___/ \_,_|\___|/__/ \__||_||_||_||_|\___|(_)/_\_\ \_, |/__|
|
|||
|
_ ___ ___ _ __ ___ __ ___ _ __|__/
|
|||
|
/ |/ _ \|_ ) / | / / ( _ ) / \ |_ )/ | / /
|
|||
|
| |\_, / / / _ | |/ _ \/ _ \ _| () |_ / / | |/ _ \
|
|||
|
|_| /_/ /___|(_)|_|\___/\___/(_)\__/(_)/___||_|\___/
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Test connexion</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh -p 55216 -i ~/.ssh/alpine-vm aluser@192.168.0.216
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h2 id="utilitaires">Utilitaires</h2>
|
|||
|
|
|||
|
<h3 id="iproute2">iproute2</h3>
|
|||
|
|
|||
|
<p>Vous pouvez souhaiter installer le paquet ‘iproute2’ (à noter que cela installera également iptables si ce n’est pas encore installé)</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apk ajouter iproute2
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Cela fournit la commande ‘ss’ qui est IMHO une version ‘meilleure’ de netstat.</p>
|
|||
|
|
|||
|
<p>Afficher les ports tcp d’écoute</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ss -tl
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
|
|||
|
LISTEN 0 0 0.0.0.0:55216 0.0.0.0:*
|
|||
|
LISTEN 0 0 127.0.0.1:2019 0.0.0.0:*
|
|||
|
LISTEN 0 0 127.0.0.1:9000 0.0.0.0:*
|
|||
|
LISTEN 0 0 *:55216 *:*
|
|||
|
LISTEN 0 0 *:https *:*
|
|||
|
LISTEN 0 0 *:http *:*
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Afficher les ports d’écoute tcp et les processus associés:</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ss -ptl
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Afficher l’écoute et les connexions tcp établies :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ss -ta
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
|
|||
|
LISTEN 0 0 0.0.0.0:55216 0.0.0.0:*
|
|||
|
LISTEN 0 0 127.0.0.1:2019 0.0.0.0:*
|
|||
|
LISTEN 0 0 127.0.0.1:9000 0.0.0.0:*
|
|||
|
ESTAB 0 0 192.168.0.216:55216 192.168.0.39:60300
|
|||
|
ESTAB 0 0 192.168.0.216:56326 192.168.0.215:microsoft-ds
|
|||
|
LISTEN 0 0 *:55216 *:*
|
|||
|
LISTEN 0 0 *:https *:*
|
|||
|
LISTEN 0 0 *:http *:*
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Afficher le résumé d’utilisation de la socket :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ss -s
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Total: 17
|
|||
|
TCP: 8 (estab 2, closed 0, orphaned 0, timewait 0)
|
|||
|
|
|||
|
Transport Total IP IPv6
|
|||
|
RAW 0 0 0
|
|||
|
UDP 1 0 1
|
|||
|
TCP 8 5 3
|
|||
|
INET 9 5 4
|
|||
|
FRAG 0 0 0
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Afficher plus d’options :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ss -h
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="drill">drill</h3>
|
|||
|
|
|||
|
<p>Vous pouvez également vouloir installer ‘drill’ (il installera également le paquet ‘ldns’) qui est un remplacement supérieur (IMHO) pour nslookup et dig etc</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apk add drill
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Alors utilisez-le comme vous le feriez avec dig</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>drill @8.8.8.8 alpinlinux.org
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour effectuer une recherche inversée (obtenir un nom depuis une IP) utilisez la syntaxe suivante :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>drill @208.67.222.222 -x 8.8.8.8
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="postfix">postfix</h3>
|
|||
|
|
|||
|
<p><em>Envoyer des messages en ligne de commande depuis le serveur alpine linux</em></p>
|
|||
|
|
|||
|
<p>Installation postfix et mailx</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apk add postfix
|
|||
|
sudo apk add mailx
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajouter les lignes suivantes au fichier de configuration <code class="language-plaintext highlighter-rouge">/etc/postfix/main.cf</code></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>myhostname = ouestline.xyz
|
|||
|
mydomain = ouestline.xyz
|
|||
|
myorigin = $mydomain
|
|||
|
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain,
|
|||
|
inet_interfaces = loopback-only
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Recharger postfix</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo service postfix restart
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Envoyer un message</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>echo "Test envoi via postfix smtp" | mail -s "serveur alpine linux" yack@cinay.eu
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Sur la boîte de réception<br />
|
|||
|
<img src="/images/alpine-ouest17.png" alt="" /></p>
|
|||
|
|
|||
|
<h3 id="chroot">chroot</h3>
|
|||
|
|
|||
|
<p><em>On veut modifier l’interface réseau de la Machine virtuelle AlpineOuest</em></p>
|
|||
|
|
|||
|
<p>Il faut arrêter la machine virtuelle AlpineOuest pour la faire redémarrer sur une image ISO<br />
|
|||
|
<img src="/images/alpine-chroot01.png" alt="" /></p>
|
|||
|
|
|||
|
<p>Booter sur une image ISO Alpine Linux</p>
|
|||
|
|
|||
|
<p>Passer le clavier en fr (qwerty de base)<br />
|
|||
|
<img src="/images/alpine-chroot03.png" alt="" /><br />
|
|||
|
<img src="/images/alpine-chroot04.png" alt="" /></p>
|
|||
|
|
|||
|
<p>Montage de la partion à chrooter /dev/vda3</p>
|
|||
|
|
|||
|
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mount /dev/vda3 /mnt
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Passage en chroot</p>
|
|||
|
|
|||
|
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">chroot</span> /mnt /bin/ash
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><img src="/images/alpine-chroot02.png" alt="" /></p>
|
|||
|
|
|||
|
<p>Modifier l’adresse<br />
|
|||
|
<img src="/images/alpine-chroot05.png" alt="" /></p>
|
|||
|
|
|||
|
<p>Sortie chroot et arrêt</p>
|
|||
|
|
|||
|
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">exit
|
|||
|
</span>poweroff
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Modifier l’ordre de démarrage et désactiver le cd-rom<br />
|
|||
|
<img src="/images/alpine-chroot06.png" alt="" /></p>
|
|||
|
|
|||
|
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<div class="d-print-none"><footer class="article__footer"><meta itemprop="dateModified" content="2023-12-22T00: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="/2023/12/17/Installer_KVM_Kernel_Virtual_Machine_sur_un_serveur.html">Installer KVM (Kernel Virtual Machine) sur un serveur</a></div><div class="next"><span>SUIVANT</span><a href="/2023/12/22/Caddy_serveur.html">Caddy serveur</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>
|
|||
|
|