<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet title="XSL formatting" type="text/xsl" href="https://uname.pingveno.net/blog/index.php/feed/rss2/xslt" ?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title># uname -a</title>
    <link>https://uname.pingveno.net/blog/index.php/</link>
    <atom:link href="https://uname.pingveno.net/blog/index.php/feed/rss2" rel="self" type="application/rss+xml" />
    <description>Le blog de uname.pingveno.net</description>
    <language>fr</language>
    <pubDate>Wed, 01 Apr 2026 16:19:15 +0200</pubDate>
    <copyright>Mathieu Pellegrin</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>Dotclear</generator>
          <item>
        <title>WordPress : Migrate from The Events Calendar to Events Manager</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2026/03/31/WordPress-%3A-Migrate-from-The-Events-Calendar-to-Events-Manager</link>
        <guid isPermaLink="false">urn:md5:72954030b3d2a914e23c5982c18cf758</guid>
        <pubDate>Tue, 31 Mar 2026 18:09:00 +0200</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Informatique</category>
                        <description>&lt;p&gt;A quick snippet to switch all my events created with &lt;a href=&quot;https://fr.wordpress.org/plugins/the-events-calendar/&quot;&gt;The Events Calendar&lt;/a&gt; to &lt;a href=&quot;https://fr.wordpress.org/plugins/events-manager/&quot;&gt;Events Manager&lt;/a&gt;, which offers more functionnalities in its free version.&lt;/p&gt;

&lt;p&gt;It is a single-file plugin that you can activate in WordPress. Use it at your own risks, and with a backup available before starting.&lt;/p&gt;          &lt;pre&gt;
&amp;lt;?php
/**
 * Plugin Name: TEC → Events Manager Migration
 * Description: Auto migration of The Events Calendar to Events Manager.
 * Version: 1.0
 */

if (!defined('ABSPATH')) exit;

class TEC_To_EM_Migration {

    const DRY_RUN = false;
    const LOG_FILE = WP_CONTENT_DIR . '/tec-em-migration.log';

    public function __construct() {
        add_action('admin_menu', [$this, 'add_admin_page']);
    }

    public function add_admin_page() {
        add_menu_page(
            'Migration TEC → EM',
            'TEC → EM',
            'manage_options',
            'tec-em-migration',
            [$this, 'admin_page']
        );
    }

    public function admin_page() {
        if (isset($_POST['run_migration'])) {
            $this-&amp;gt;log(&quot;=== START MIGRATION ===&quot;);
            $this-&amp;gt;migrate_events();
            $this-&amp;gt;log(&quot;=== END MIGRATION ===&quot;);
            echo &quot;&amp;lt;div class='updated'&amp;gt;&amp;lt;p&amp;gt;Migration finished. See log file.&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&quot;;
        }

        echo '&amp;lt;h1&amp;gt;Migration TEC → Events Manager&amp;lt;/h1&amp;gt;';
        echo '&amp;lt;form method=&quot;post&quot;&amp;gt;';
        echo '&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;DRY RUN :&amp;lt;/strong&amp;gt; ' . (self::DRY_RUN ? 'YES' : 'NO') . '&amp;lt;/p&amp;gt;';
        echo '&amp;lt;input type=&quot;submit&quot; name=&quot;run_migration&quot; class=&quot;button button-primary&quot; value=&quot;Start migration&quot;&amp;gt;';
        echo '&amp;lt;/form&amp;gt;';
    }

    private function migrate_events() {
        $events = get_posts([
            'post_type'      =&amp;gt; 'tribe_events',
            'post_status' =&amp;gt; 'publish',
            'posts_per_page' =&amp;gt; -1,
        ]);

        foreach ($events as $event) {
            $this-&amp;gt;log(&quot;The Event Calendar: {$event-&amp;gt;post_title}&quot;);

            // Dates
            $start = get_post_meta($event-&amp;gt;ID, '_EventStartDate', true);
            $end   = get_post_meta($event-&amp;gt;ID, '_EventEndDate', true);

            // Location
            $venue_id = get_post_meta($event-&amp;gt;ID, '_EventVenueID', true);
            $location_id = $this-&amp;gt;migrate_location($venue_id);

            // Create EM event
            $new_post_id = $this-&amp;gt;create_em_event($event, $start, $end, $location_id);

            // Thumbnail
            $thumbnail_id = get_post_thumbnail_id($event-&amp;gt;ID);
            set_post_thumbnail($new_post_id, $thumbnail_id);
        }
    }

    private function migrate_location($venue_id) {
        if (!$venue_id) return 0;

        $venue = get_post($venue_id);
        if (!$venue) return 0;

        $address = get_post_meta($venue_id, '_VenueAddress', true);
        $city    = get_post_meta($venue_id, '_VenueCity', true);
        $country = 'FR';

        $this-&amp;gt;log(&quot; → Location: {$venue-&amp;gt;post_title}&quot;);

        if (self::DRY_RUN) return 0;

        global $wpdb;
        $existing_id = $wpdb-&amp;gt;get_var(
            $wpdb-&amp;gt;prepare(
                &quot;SELECT location_id FROM {$wpdb-&amp;gt;prefix}em_locations WHERE location_name = %s LIMIT 1&quot;, $venue-&amp;gt;post_title
            )
        );
 
        if ($existing_id) {
            $this-&amp;gt;log(&quot;   → Existing location, ID = {$existing_id}&quot;);
            return $existing_id;
        } else {

            $EM_Location = new EM_Location();

            $EM_Location-&amp;gt;location_name    = $venue-&amp;gt;post_title;
            $EM_Location-&amp;gt;location_address = $address;
            $EM_Location-&amp;gt;location_town    = $city;
            $EM_Location-&amp;gt;location_country = $country;

            $lat = get_post_meta($venue_id, '_VenueLatitude', true);
            $lng = get_post_meta($venue_id, '_VenueLongitude', true);
            if ($lat &amp;amp;&amp;amp; $lng) {
                $EM_Location-&amp;gt;location_latitude  = $lat;
                $EM_Location-&amp;gt;location_longitude = $lng;
            }

            $EM_Location-&amp;gt;save();

            $this-&amp;gt;log(&quot;   → New location created, ID = {$EM_Location-&amp;gt;location_id}&quot;);

            return $EM_Location-&amp;gt;location_id;
        }
    }

    private function extract_time($datetime) {
        if (!$datetime) return '00:00:00';

        // Normalize datetime
        $datetime = str_replace('T', ' ', $datetime);

        // Extract hours
        $parts = explode(' ', trim($datetime));
        if (count($parts) &amp;lt; 2) return '00:00:00';

        $time = $parts[1];

        // Add missing seconds
        if (preg_match('/^\d{2}:\d{2}$/', $time)) {
            $time .= ':00';
        }

        // Only keep HH:MM:SS
        if (preg_match('/^\d{2}:\d{2}:\d{2}/', $time, $m)) {
            return $m[0];
        }

        return '00:00:00';
    }

    private function create_em_event($event, $start, $end, $location_id) {
        $this-&amp;gt;log(&quot; → Creating EM: {$event-&amp;gt;post_title}&quot;);

        $start_date = substr($start, 0, 10);
        $end_date   = substr($end, 0, 10);

        $start_time = $this-&amp;gt;extract_time($start);
        $end_time   = $this-&amp;gt;extract_time($end);

        if (self::DRY_RUN) return 0;

        // Create new EM event

        $EM_Event = new EM_Event();
        $EM_Event-&amp;gt;event_type = 'single';
        $EM_Event-&amp;gt;event_archetype = 'event';
        $EM_Event-&amp;gt;event_name = $event-&amp;gt;post_title;
        $EM_Event-&amp;gt;post_content = $event-&amp;gt;post_content;
        $EM_Event-&amp;gt;event_date_created = $event-&amp;gt;post_date;
        $EM_Event-&amp;gt;event_start_date = $start_date;
        $EM_Event-&amp;gt;event_start_time = $start_time;
        $EM_Event-&amp;gt;event_end_date = $end_date;
        $EM_Event-&amp;gt;event_end_time = $end_time;
        $EM_Event-&amp;gt;start = strtotime($EM_Event-&amp;gt;event_start_date.' '.$EM_Event-&amp;gt;event_start_time);
        $EM_Event-&amp;gt;end = strtotime($EM_Event-&amp;gt;event_end_date.' '.$EM_Event-&amp;gt;event_end_time);
        $EM_Event-&amp;gt;event_rsvp = false;
        $EM_Event-&amp;gt;event_rsvp_time = $start_time;
        $EM_Event-&amp;gt;location_id = $location_id;
        $EM_Event-&amp;gt;event_status = 1;
        $EM_Event-&amp;gt;save();

        return $EM_Event-&amp;gt;post_id;
    }

    private function log($msg) {
        file_put_contents(self::LOG_FILE, date('[Y-m-d H:i:s] ') . $msg . &quot;\n&quot;, FILE_APPEND);
    }
}

new TEC_To_EM_Migration();&lt;/pre&gt;</description>
        
              </item>
          <item>
        <title>Drupal 8 / 9 / 10 : Programmatically render a view with contextual and exposed filters input</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2024/03/10/Drupal-8-/-9-/-10-%3A-The-right-way-to-programatically-render-a-view-while-setting-exposed-and-contextual-filters-input</link>
        <guid isPermaLink="false">urn:md5:28fceab459140a8a74e0ec4b7befc8fc</guid>
        <pubDate>Sun, 10 Mar 2024 19:45:00 +0100</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Informatique</category>
                          <category>drupal</category>
                  <category>drupal 10</category>
                  <category>drupal 8</category>
                  <category>drupal 9</category>
                  <category>php</category>
                <description>&lt;p&gt;&lt;strong&gt;Exposed Input&lt;/strong&gt; and &lt;strong&gt;Contextual Input&lt;/strong&gt; are two different ways of providing input to Drupal Views.&lt;/p&gt;

&lt;p&gt;Contextual filters work with an ordered list of parameters, while Exposed Input works with a form that has a couple name/value for every input parameter.&lt;/p&gt;          &lt;h2&gt;Contextual Filters&lt;/h2&gt;

&lt;p&gt;The right way to render a view result with &lt;strong&gt;contextual filters&lt;/strong&gt; is to generate a render array:&lt;/p&gt;

&lt;blockquote&gt;
&lt;pre&gt;
$render_array = [
  '#type' =&amp;gt; 'view',
  '#name' =&amp;gt; 'YOUR_VIEW_NAME',
  '#display_id' =&amp;gt; 'YOUR_VIEW_DISPLAY',
  '#arguments' =&amp;gt; [CONTEXTUAL_FILTER_1, CONTEXTUAL_FILTER_2, ...],
];&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can then send the render array to a Twig variable, or render it programatically:&lt;/p&gt;

&lt;blockquote&gt;
&lt;pre&gt;
$result = \Drupal::service('renderer')-&amp;gt;render($render_array);
return $result-&amp;gt;__toString();&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;We are using the &lt;code&gt;__toString()&lt;/code&gt; function to get the rendered HTML because the result returned by Drupal Render service is an object containing cache metadata.&lt;/p&gt;

&lt;h2&gt;Exposed Filters&lt;/h2&gt;

&lt;p&gt;The right way to render a view result with &lt;strong&gt;exposed filters&lt;/strong&gt; is to generate a render array, and set the &lt;code&gt;'#view#'&lt;/code&gt; parameter with a view object where you can initialize the filters:&lt;/p&gt;

&lt;blockquote&gt;
&lt;pre&gt;
$view = \Drupal\views\Views::getView('YOUR_VIEW_NAME');
$view-&amp;gt;setExposedInput([
    'YOUR_FILTER_NAME' =&amp;gt; 'YOUR_FILTER_VALUE',
]);
$render_array = [
  '#type' =&amp;gt; 'view',
  '#name' =&amp;gt; 'YOUR_VIEW_NAME',
  '#view' =&amp;gt; $view,
  '#display_id' =&amp;gt; 'YOUR_VIEW_DISPLAY',
  '#arguments' =&amp;gt; [CONTEXTUAL_FILTER_1, CONTEXTUAL_FILTER_2, ...],
];&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can then send the render array to a Twig variable, or render it programatically:&lt;/p&gt;

&lt;blockquote&gt;
&lt;pre&gt;
$result = \Drupal::service('renderer')-&amp;gt;render($render_array);
return $result-&amp;gt;__toString();&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;We are using the &lt;code&gt;__toString()&lt;/code&gt; function to get the rendered HTML because the result returned by Drupal Render service is an object containing cache metadata.&lt;/p&gt;

&lt;h2&gt;Leaked Metadata and Early Rendering&lt;/h2&gt;

&lt;p&gt;If you render a view while rendering a controller output that is suppose&amp;nbsp; to provide its own cache metadata (&lt;code&gt;CacheableJsonResponse&lt;/code&gt; for instance), and run on the error:&lt;/p&gt;

&lt;blockquote&gt;
&lt;pre&gt;
LogicException: The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early.&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;Just wrap the rendering in a render context:&lt;/p&gt;

&lt;blockquote&gt;
&lt;pre&gt;
$context = new Drupal\Core\Render\RenderContext\RenderContext();
$html = \Drupal::service('renderer')-&amp;gt;executeInRenderContext($context, function () {
  $render_array = [
    '#type' =&amp;gt; 'view',
    '#name' =&amp;gt; 'YOUR_VIEW_NAME',
    '#display_id' =&amp;gt; 'YOUR_VIEW_DISPLAY',
    '#arguments' =&amp;gt; [CONTEXTUAL_FILTER_1, CONTEXTUAL_FILTER_2, ...],
  ];
  $result = \Drupal::service('renderer')-&amp;gt;render($render_array);
  return $result-&amp;gt;__toString();
});
&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;h2&gt;Sources&lt;/h2&gt;

&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;https://drupal.stackexchange.com/a/295496&quot;&gt;https://drupal.stackexchange.com/a/295496&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://www.drupal.org/docs/drupal-apis/render-api/render-arrays&quot;&gt;https://www.drupal.org/docs/drupal-apis/render-api/render-arrays&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://api.drupal.org/api/drupal/10/search/setExposedInput&quot;&gt;https://api.drupal.org/api/drupal/10/search/setExposedInput&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://www.lullabot.com/articles/early-rendering-a-lesson-in-debugging-drupal-8&quot;&gt;https://www.lullabot.com/articles/early-rendering-a-lesson-in-debugging-drupal-8&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
        
              </item>
          <item>
        <title>Configurer Visual Studio Code pour utiliser xdebug avec PHP FPM et SSHFS</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2021/05/28/Configurer-Visual-Studio-Code-pour-utiliser-xdebug-avec-PHP-FPM-et-SSHFS</link>
        <guid isPermaLink="false">urn:md5:82c8f6d6034f3c20d6ad67f5a7f06d52</guid>
        <pubDate>Fri, 28 May 2021 14:46:00 +0200</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Informatique</category>
                        <description>          &lt;p&gt;Bonjour,&lt;/p&gt;

&lt;p&gt;Une note à moi-même et à mes stagiaires pour utiliser xdebug avec un point de montage SSHFS, et PHP FPM.&lt;/p&gt;

&lt;ol&gt;
	&lt;li&gt;S'assurer que PHP est bien installé sur la machine du développeur, c'est nécessaire pour la coloration syntaxique et la vérification de syntaxe.&lt;/li&gt;
	&lt;li&gt;Installer PHP xdebug sur la machine distante&lt;/li&gt;
	&lt;li&gt;Configurer xdebug pour PHP FPM sur la machine distante. Attention, cela change selon la version de xdebug.
	&lt;ol&gt;
		&lt;li&gt;Modifier le fichier de configuration FPM pour xdebug, par exemple pour Debian c'est &lt;strong&gt;/etc/php/7.3/fpm/conf.d/20-xdebug.ini&lt;/strong&gt;&lt;/li&gt;
		&lt;li&gt;Y placer les instructions suivantes :&lt;br /&gt;
		Pour xdebug v3 :
		&lt;pre&gt;
&lt;code&gt;[xdebug]
zend_extension=&quot;xdebug.so&quot;
xdebug.mode&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;debug&lt;/code&gt;
&lt;code class=&quot;language-ini&quot;&gt;xdebug.start_with_request = yes&lt;/code&gt;
&lt;code&gt;xdebug.client_host=127.0.0.1
xdebug.client_port=&quot;9003&quot;&lt;/code&gt;
&lt;/pre&gt;
		Pour xdebug v2 :

		&lt;pre&gt;
&lt;code&gt;
[xdebug]
zend_extension=&quot;xdebug.so&quot;
xdebug.remote_enable=1
xdebug.remote_autostart=1
xdebug.remote_host=127.0.0.1
xdebug.remote_port=&quot;9003&quot;&lt;/code&gt;&lt;/pre&gt;
		&lt;/li&gt;
		&lt;li&gt;Redémarrer PHP FPM.&lt;br /&gt;
		 &lt;/li&gt;
	&lt;/ol&gt;
	&lt;/li&gt;
	&lt;li&gt;Sur la machine du développeur, installer le module PHP xdebug pour Visual Studio : &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug&quot;&gt;https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;Connecter en SSHFS le dossier distant (la machine qui exécute PHP) sur la machine (locale) du développeur : sshfs login@distant:dossier_distant dossier_local&lt;/li&gt;
	&lt;li&gt;Connecter un tunnel SSH pour forward le port xdebug vers la machine locale : ssh&lt;code&gt; -R &lt;span class=&quot;token number&quot;&gt;9003&lt;/span&gt;:localhost:9003&lt;/code&gt; login@distant&lt;/li&gt;
	&lt;li&gt;Ouvrir dans Visual Studio le dossier monté avec SSHFS&lt;/li&gt;
	&lt;li&gt;Ouvrir un fichier, puis le debugger dans les onglets à gauche&lt;/li&gt;
	&lt;li&gt;Cliquer sur &quot;create a json launch file&quot;, et vérifier le port de xdebug pour pointer sur le bon port. Ajouter le pathMapping pour mettre en correspondance le chemin local (ouvert dans Visual Studio Code) et le chemin distant (monté en SSHFS) :
	&lt;pre&gt;
&lt;code&gt;{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    &quot;version&quot;: &quot;0.2.0&quot;,
    &quot;configurations&quot;: [
        {
            &quot;name&quot;: &quot;Listen for Xdebug&quot;,
            &quot;type&quot;: &quot;php&quot;,
            &quot;request&quot;: &quot;launch&quot;,
            &quot;port&quot;: 9003,
            &quot;pathMappings&quot;: {
                &quot;&lt;/code&gt;dossier_distant&lt;code&gt;&quot;: &quot;${workspaceFolder}/&quot;
            }
        },
        {
            &quot;name&quot;: &quot;Launch currently open script&quot;,
            &quot;type&quot;: &quot;php&quot;,
            &quot;request&quot;: &quot;launch&quot;,
            &quot;program&quot;: &quot;${file}&quot;,
            &quot;cwd&quot;: &quot;${fileDirname}&quot;,
            &quot;port&quot;: 9003,
            &quot;pathMappings&quot;: {
                &quot;chemin_distant&quot;: &quot;${workspaceFolder}/&quot;
            }
        }
    ]
}&lt;/code&gt;&lt;/pre&gt;
	&lt;/li&gt;
	&lt;li&gt;Redémarrer Visual Studio par sécurité.&lt;/li&gt;
	&lt;li&gt;Lancer l'écoute via le debugger, ajouter les breakpoints dans Visual Studio, puis charger la page du site distant via le navigateur. Visual Studio devrait stopper l'exécution au niveau des breakpoints, pour permettre l'affichage de la pile, et le lancement des commandes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;Sources&lt;/h3&gt;

&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;https://www.jetbrains.com/help/phpstorm/remote-debugging-via-ssh-tunnel.html#prepare-the-debugging-engine&quot;&gt;https://www.jetbrains.com/help/phpstorm/remote-debugging-via-ssh-tunnel.html#prepare-the-debugging-engine&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/languages/php&quot;&gt;https://code.visualstudio.com/docs/languages/php&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;</description>
        
              </item>
          <item>
        <title>Configure a 3CX extension with Big Blue Button conferencing bridge (Freeswitch)</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2020/10/23/Configure-a-3CX-extension-with-Big-Blue-Button-conferencing-bridge-%28Freeswitch%29</link>
        <guid isPermaLink="false">urn:md5:c6d5cd185f79ab65b59e18b2078e003a</guid>
        <pubDate>Fri, 23 Oct 2020 16:43:00 +0200</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Hacks</category>
                          <category>3cx</category>
                  <category>bigbluebutton</category>
                  <category>freepbx</category>
                  <category>freeswitch</category>
                <description>          &lt;p&gt;Just a quick note for reference.&lt;/p&gt;

&lt;p&gt;I managed to successfully configure the phone bridge with FreePBX as a SIP provider for Big Blue Button using this documentation : &lt;a href=&quot;https://docs.bigbluebutton.org/2.2/customize.html#add-a-phone-number-to-the-conference-bridge&quot; hreflang=&quot;en&quot;&gt;https://docs.bigbluebutton.org/2.2/customize.html#add-a-phone-number-to-the-conference-bridge&lt;/a&gt; . My trunk is connectd to the SIP server running FreePBX and Big Blue Button is registering as a phone extension to receive phone calls and route them to the conference room.&lt;/p&gt;

&lt;p&gt;But using a similar architecture with 3CX instead of FreePBX as a provider was failing. Actually, you have to add the AuthID as &quot;auth-username&quot; attribute (see &lt;a href=&quot;https://freeswitch.org/confluence/display/FREESWITCH/Sofia+Gateway+Authentication+Params&quot;&gt;https://freeswitch.org/confluence/display/FREESWITCH/Sofia+Gateway+Authentication+Params&lt;/a&gt; )&lt;/p&gt;

&lt;p&gt;In Big Blue Button, the profile file would looks like :&lt;/p&gt;

&lt;pre&gt;
&amp;lt;include&amp;gt;
  &amp;lt;gateway name=&quot;ANY-NAME-FOR-YOUR-PROVIDER&quot;&amp;gt;
    &amp;lt;param name=&quot;proxy&quot; value=&quot;sip.example.net&quot;/&amp;gt;
    &amp;lt;param name=&quot;username&quot; value=&quot;EXTENSION NUMBER (for instance 100)&quot;/&amp;gt;
    &amp;lt;param name=&quot;auth-username&quot; value=&quot;THE AUTHID&quot;/&amp;gt;
    &amp;lt;param name=&quot;password&quot; value=&quot;PASSWORD&quot;/&amp;gt;
    &amp;lt;param name=&quot;extension&quot; value=&quot;CALLED-NUMBER&quot;/&amp;gt;
    &amp;lt;param name=&quot;register&quot; value=&quot;true&quot;/&amp;gt;
    &amp;lt;param name=&quot;context&quot; value=&quot;public&quot;/&amp;gt;
  &amp;lt;/gateway&amp;gt;
&amp;lt;/include&amp;gt;&lt;/pre&gt;

&lt;p&gt;I hope it will help someone stuck with the official documentation.&lt;/p&gt;</description>
        
              </item>
          <item>
        <title>Redémarrer une machine lorsqu'un programme l'étouffe inopinément (Debian 10)</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2020/10/15/Red%C3%A9marrer-une-machine-lorsqu-un-programme-l-%C3%A9touffe-inopin%C3%A9ment-%28Debian-10%29</link>
        <guid isPermaLink="false">urn:md5:2df2a5ec71b76a25b469cd103931185c</guid>
        <pubDate>Wed, 14 Oct 2020 12:40:00 +0200</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Hacks</category>
                          <category>debian</category>
                  <category>limits</category>
                  <category>linux</category>
                  <category>memory</category>
                  <category>overflow</category>
                  <category>reboot</category>
                  <category>watchdog</category>
                <description>          &lt;p&gt;Admettons que vous ayez une machine avec un programme bugué. Typiquement, un logiciel métier avec une fuite de mémoire, qui va pour une raison inconnue saturer la mémoire vive et provoquer son propre crash ou le crash des autres services sur la machine.&lt;/p&gt;

&lt;p&gt;Vous pourriez lui mettre une limite de mémoire via &lt;a href=&quot;https://linux.die.net/man/5/limits.conf&quot; hreflang=&quot;en&quot;&gt;/etc/security/limits.conf&lt;/a&gt; mais ça ne fera que planter le programme (segmentation fault) pour protéger les autres, et dans le cas d'un logiciel métier cela veut dire prendre ensuite d'autres actions, manuelles ou automatiques, via un système de monitoring approprié.&lt;/p&gt;

&lt;p&gt;C'est vrai, le monitoring est la bonne solution quand on suit un logiciel aussi critique qu'un logiciel métier, mais dans le cas d'un logiciel qui doit &quot;juste tourner&quot;, d'une équipe de maintenance réduite ou n'ayant pas la possibilité d'astreintes, une solution simple pour s'assurer que la machine tourne même en cas d'incident, c'est de redémarrer la machine lors d'un incident. Pour cela, on peut utiliser le programme &lt;strong&gt;watchdog&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Cela doit être fait avec motivations et circonspection, une analyse &quot;post-mortem&quot; doit suivre chaque reboot, dans le cas d'une tentative d'élévation de privilèges par Buffer Overflow, rien ne dit que l'attaque n'a pas réussie même si le système a redémarré.&lt;/p&gt;

&lt;h3&gt;Watchdog : Configuration&lt;/h3&gt;

&lt;p&gt;Watchdog s'installe via les paquets :&lt;/p&gt;

&lt;pre&gt;
apt install watchdog&lt;/pre&gt;

&lt;p&gt;Il y a ensuite deux choses à configurer : les règles pour déclencher le reboot, et le mode de chargement du programme (&quot;no actions&quot; ou actif).&lt;/p&gt;

&lt;p&gt;Dans mon cas, je voulais redémarrer si le serveur avait moins de 20% de RAM disponible, ce serveur tourne en permanence à 50% de consommation de RAM et n'est jamais sensé dépasser, ou alors c'est signe d'un problème, d'où le reboot à déclencher.&lt;/p&gt;

&lt;p&gt;La configuration de watchdog se trouve dans &lt;strong&gt;/etc/watchdog.conf&lt;/strong&gt; et fournit deux valeurs à régler : &lt;strong&gt;min-memory&lt;/strong&gt; et &lt;strong&gt;allocatable-memory&lt;/strong&gt; . Attention, ces valeurs sont à renseigner en &lt;strong&gt;taille de page RAM&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Pour identifier la taille des pages sur le système, utilisez la commande :&lt;/p&gt;

&lt;pre&gt;
getconf PAGESIZE&lt;/pre&gt;

&lt;p&gt;Ensuite, un simple calcul permet d'identifier la valeur à mettre dans min-memory et allocatable-memory. Par exemple, pour un PAGESIZE de 4096 (4 kB), 2 GB (2048 MB) de RAM et 20% de ce total :&lt;/p&gt;

&lt;pre&gt;
( 20 % * 2048 * 1000 ) / 4096&lt;/pre&gt;

&lt;p&gt;Une fois &lt;strong&gt;min-memory&lt;/strong&gt; et &lt;strong&gt;allocatable-memory&lt;/strong&gt; configurés, il convient de tester le trigger avant de le démarrer;&lt;/p&gt;

&lt;p&gt;Les options de lancement se trouvent dans &lt;strong&gt;/etc/default/watchdog&lt;/strong&gt; . Modifier &lt;strong&gt;watchdog_options&lt;/strong&gt; pour y renseigner :&lt;/p&gt;

&lt;pre&gt;
watchdog_options=&quot;-v --no-action&quot;&lt;/pre&gt;

&lt;p&gt;Désactivez Watchdog du démarrage automatique, puis démarrez-le :&lt;/p&gt;

&lt;pre&gt;
systemctl disable watchdog
service watchdog start&lt;/pre&gt;

&lt;p&gt;Vous en verrez la trace dans les logs via &lt;strong&gt;service watchdog status&lt;/strong&gt; ou &lt;strong&gt;journalctl -f&lt;/strong&gt; .&lt;/p&gt;

&lt;h3&gt;Watchdog : Activation&lt;/h3&gt;

&lt;p&gt;Si les essais sont concluants, retournez dans &lt;strong&gt;/etc/default/watchdog&lt;/strong&gt; pour activer le module noyau. Cela évitera que Watchdog se fasse tuer par un processus quelconque (oom-killer par exemple) :&lt;/p&gt;

&lt;pre&gt;
watchdog_module=&quot;softdog&quot;&lt;/pre&gt;

&lt;p&gt;Si vous ne voulez laisser aucune chance pour éviter le reboot (par exemple si oom-killer a réussi à baisser la consommation de ram sous le seuil acceptable et que le reboot n'est plus nécessaire), rajoutez &lt;strong&gt;nowayout&lt;/strong&gt; :&lt;/p&gt;

&lt;pre&gt;
&lt;code&gt;watchdog_module=&quot;softdog nowayout&quot;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Attention, une fois chargé en &lt;strong&gt;nowayout&lt;/strong&gt;, le module noyau restera actif même si watchdog est stoppé (légitimement ou non), le seul moyen de le retirer est de retirer softdog des modules noyau (via &lt;strong&gt;rmmod&lt;/strong&gt;), ou de rebooter.&lt;/p&gt;

&lt;p&gt;Activez ensuite watchdog :&lt;/p&gt;

&lt;pre&gt;
systemctl enable watchdog
service watchdog start&lt;/pre&gt;

&lt;p&gt;watchdog doit être visible dans les modules noyau chargés :&lt;/p&gt;

&lt;pre&gt;
lsmod | grep softdog&lt;/pre&gt;

&lt;h3&gt;Sources&lt;/h3&gt;

&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;https://www.cyberciti.biz/faq/linux-check-the-size-of-pagesize/&quot; hreflang=&quot;en&quot;&gt;Page size check&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://www.supertechcrew.com/watchdog-keeping-system-always-running/&quot; hreflang=&quot;en&quot;&gt;Watchdog configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;</description>
        
              </item>
          <item>
        <title>Maman, j'ai patché Debian</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2020/03/19/Maman-j-ai-patch%C3%A9-Debian</link>
        <guid isPermaLink="false">urn:md5:71a75a5565e36a22a1a7aac2045ae2d7</guid>
        <pubDate>Thu, 19 Mar 2020 15:33:00 +0100</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Hacks</category>
                          <category>compile</category>
                  <category>debian</category>
                  <category>debuild</category>
                  <category>package</category>
                  <category>patch</category>
                <description>          &lt;p&gt;Un billet en forme de note à moi-même sur ce qu'il faut faire pour correctement :&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;Récupérer un package depuis upstream&lt;/li&gt;
	&lt;li&gt;Appliquer un ou plusieurs patches&lt;/li&gt;
	&lt;li&gt;Le signer et le re-déployer en production&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Préparer l'environnement&lt;/h3&gt;

&lt;p&gt;Pour compiler et signer un paquet simplement il vous faut :&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;Un utilisateur (non root)&lt;/li&gt;
	&lt;li&gt;Une clé GPG&lt;/li&gt;
	&lt;li&gt;Les build-essentials et les devscripts (parce qu'on est feignasse)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Créez un utilisateur non-root et loguez-vous avec. N'utilisez pas &quot;su&quot; à partir de root, parce que sinon GPG ne pourra pas vous demander la phrase de passe (des histoires de droits sur les TTY).&lt;/p&gt;

&lt;p&gt;Créez une clé GPG, et paramétrez-la :&lt;/p&gt;

&lt;pre&gt;
gpg --full-generate-key&lt;/pre&gt;

&lt;p&gt;Récupérez l'identifiant de clé à la fin de la procédure, ou avec &lt;strong&gt;gpg&amp;nbsp;--list-keys&lt;/strong&gt; si vous l'avez loupé.&lt;/p&gt;

&lt;p&gt;Modifiez ou créez le fichier &lt;strong&gt;~/.devscripts&lt;/strong&gt; et ajoutez :&lt;/p&gt;

&lt;pre&gt;
DEBUILD_SET_ENVVAR_DEBSIGN_KEYID=xxxxxxxx
&lt;/pre&gt;

&lt;p&gt;Avec le xxxxxx qui correspond à votre identifiant de clé.&lt;/p&gt;

&lt;h3&gt;Récupérer le paquet et les dépendances de compilation&lt;/h3&gt;

&lt;p&gt;Le plus simple c'est quand le paquet existe déjà et qu'il faut simplement patcher. S'il n'existe aucun paquet, il faut créer un nouveau paquet, éventuellement debianizer la configuration, et c'est une autre paire de manches (et c'est pas le sujet ici).&lt;/p&gt;

&lt;p&gt;Pour récupérer le paquet upstream :&lt;/p&gt;

&lt;pre&gt;
apt-get source nomdupaquet&lt;/pre&gt;

&lt;p&gt;Si le paquet est introuvable, ajoutez les dépôts src à votre sources.list :&lt;/p&gt;

&lt;pre&gt;
&lt;strong&gt;deb-src&lt;/strong&gt; http://deb.debian.org/debian/ buster main contrib
&lt;strong&gt;deb-src&lt;/strong&gt; http://security.debian.org/debian-security buster/updates main contrib
&lt;strong&gt;deb-src&lt;/strong&gt; http://deb.debian.org/debian/ buster-updates main contrib&lt;/pre&gt;

&lt;p&gt;Il faut ensuite récupérer les paquets nécessaires à la compilation. Coup de bol, si vous avez pu avoir le paquet source à l'étape précédente, c'est facile :&lt;/p&gt;

&lt;pre&gt;
apt-get build-dep nomdupaquet&lt;/pre&gt;

&lt;h3&gt;Patcher le paquet&lt;/h3&gt;

&lt;p&gt;Le format dpatch est obsolète, en principe votre package utilise quilt comme tout paquet récent. Il suffit de télécharger le patch depuis git et le placer dans le dossier &lt;strong&gt;debian/patches&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Ensuite, ajoutez le nom du fichier que vous avez ajouté au fichier &lt;strong&gt;debian/patches/series&lt;/strong&gt; . Attention, l'ordre dans series est important.&lt;/p&gt;

&lt;h3&gt;Déclarer les changements&lt;/h3&gt;

&lt;p&gt;Ce n'est pas nécessaire la première fois, mais si vous re-compilez un paquet, il faut ajouter un commentaire dans le Changelog. Le plus simple : utilisez la commande &lt;strong&gt;dch -i&amp;nbsp; &lt;/strong&gt;et modifiez la ligne de changelog, en changeant bien la version du paquet pour qu'elle soit consécutive à la précédente.&lt;/p&gt;

&lt;h3&gt;Compiler le paquet&lt;/h3&gt;

&lt;p&gt;Rendez-vous dans le dossier du paquet, et lancez la commande &lt;strong&gt;debuild&lt;/strong&gt; . C'est tout. Rentrez votre phrase de passe pour la clé à la fin de la procédure.&lt;/p&gt;

&lt;h3&gt;Déployer en production&lt;/h3&gt;

&lt;p&gt;Pour déployer un paquet, deux solutions :&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;Envoyer le paquet puis l'installer avec &lt;strong&gt;dpkg -i lefichier.deb&lt;/strong&gt; ou un outil d'orchestration&lt;/li&gt;
	&lt;li&gt;Installer un DPA (Debian Private Repository) et l'ajouter au sources.list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;L'installation du serveur DPA fera l'objet d'un autre billet (un jour).&lt;/p&gt;

&lt;h3&gt;Sources&lt;/h3&gt;

&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;https://help.github.com/en/github/authenticating-to-github/generating-a-new-gpg-key&quot;&gt;Create GPG KEY&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://wiki.debian.org/debian/patches&quot;&gt;Patch a Debian package&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://www.debian.org/doc/manuals/maint-guide/build.en.html&quot;&gt;Build a Debian package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
        
              </item>
          <item>
        <title>Wordpress : la revanche du reverse proxy</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2020/01/23/Wordpress-%3A-la-revanche-du-reverse-proxy</link>
        <guid isPermaLink="false">urn:md5:7efb045bd6818357ae77177b2af27e60</guid>
        <pubDate>Thu, 23 Jan 2020 23:30:00 +0100</pubDate>
        <dc:creator>Mathieu</dc:creator>
                          <category>https</category>
                  <category>online.net</category>
                  <category>scaleway</category>
                  <category>ssl</category>
                  <category>wordpress</category>
                <description>          &lt;p&gt;Quand un reverse proxy est mal configuré,&lt;/p&gt;

&lt;p&gt;Il n'a pas les bonnes variables d'environnement,&lt;/p&gt;

&lt;p&gt;Wordpress se croit toujours en HTTP,&lt;/p&gt;

&lt;p&gt;Et fait une boucle de redirection en HTTPS.&lt;/p&gt;

&lt;p&gt;Heureusement, il y a une autre solution,&lt;/p&gt;

&lt;p&gt;Ouvrez wp-config.php, et ajoutez (au bon endroit) :&lt;/p&gt;

&lt;pre&gt;
/* Turn HTTPS 'on' if HTTP_X_FORWARDED_PROTO matches 'https' */
if (strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false) {
    $_SERVER['HTTPS'] = 'on';
}&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;&lt;a href=&quot;https://wordpress.stackexchange.com/a/250254&quot;&gt;Source&lt;/a&gt;.&lt;/code&gt;&lt;/p&gt;</description>
        
              </item>
          <item>
        <title>La délégation NS d'un sous-domaine, cet illustre inconnu</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2019/03/02/La-d%C3%A9l%C3%A9gation-NS-d-un-sous-domaine%2C-cet-illustre-inconnu</link>
        <guid isPermaLink="false">urn:md5:1b281af171b186ea2d70f7efa5c4e471</guid>
        <pubDate>Tue, 05 Mar 2019 09:15:00 +0100</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Informatique</category>
                          <category>delegation</category>
                  <category>dns</category>
                  <category>serveur</category>
                <description>&lt;p&gt;Un billet un peu technique aujourd'hui. Si vous travaillez dans le Web, vous n'êtes pas sans savoir que c'est le mécanisme des DNS qui fait en sorte que vos noms de domaine soient résolus en adresses vers votre hébergement.&lt;/p&gt;

&lt;p&gt;Vous savez aussi peut-être ce qu'est une zone DNS. Une zone DNS est un bête fichier présent sur le serveur DNS, et qui contient l'ensemble des enregistrements du domaine&lt;sup&gt;1&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Vous savez aussi certainement ce qu'est un sous-domaine, par exemple pour le domaine example.com, des sous-domaines seraient :&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;www.example.com&lt;/li&gt;
	&lt;li&gt;test.example.com&lt;/li&gt;
	&lt;li&gt;sousdomaine.example.com&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Parfois, pour séparer les services, on souhaite aller encore plus loin et dédier un sous-domaine entier à une tâche, par exemple on souhaite faire en sorte que compta.example.com soit totalement indépendant, c'est à dire :&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;Qu'on puisse recevoir sur l'email facturation@compta.example.com ,&lt;/li&gt;
	&lt;li&gt;Qu'on puisse avoir des mini-sites dédiés sous ce sous-domaine, comme urssaf.compta.example.com ou creances.compta.example.com&lt;/li&gt;
	&lt;li&gt;Que le sous-domaine puisse servir tout autre usage tel que permis par les DNS (enregistrements SRV, etc)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bien sûr, ces usages sont possibles à partir de la zone DNS du domaine parent, mais à mesure que le sous-domaine grandit en indépendance (et donc en nombre d'enregistrements), il peut être nécessaire de poser les bases d'une délégation plus avancée, pour éviter de maintenir une zone avec tous les enregistrements. On peut aussi vouloir déléguer à un prestataire la gestion de ce sous-domaine.&lt;/p&gt;

&lt;p&gt;Pour ce qui est des mini-sites, il est relativement simple de déclarer un &lt;em&gt;wildcard&lt;/em&gt; pour rediriger tous les enregistrements d'un sous-domaine vers un même serveur. Mais lorsqu'on veut plus de souplesse, ou que l'on veut aussi déléguer tous les enregistrements de ce sous-domaine, un &lt;em&gt;wildcard&lt;/em&gt; est trop &quot;brutal&quot; et pas toujours supporté par toutes les spécifications, il faut alors déléguer la zone via une délégation NS.&lt;/p&gt;          &lt;h3&gt;En deux images&lt;/h3&gt;

&lt;figure&gt;&lt;a class=&quot;media-link&quot; href=&quot;https://uname.pingveno.net/blog/public/schemas/delegation-ns/requete-dns-classique.png&quot;&gt;&lt;img alt=&quot;requete-dns-classique.png&quot; class=&quot;media&quot; src=&quot;https://uname.pingveno.net/blog/public/schemas/delegation-ns/.requete-dns-classique_m.png&quot; /&gt;&lt;/a&gt;

&lt;figcaption&gt;Requête DNS classique : le serveur répond au client avec un enregistrement A&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;a class=&quot;media-link&quot; href=&quot;https://uname.pingveno.net/blog/public/schemas/delegation-ns/requete-dns-deleguation.png&quot;&gt;&lt;img alt=&quot;requete-dns-deleguation.png&quot; class=&quot;media&quot; src=&quot;https://uname.pingveno.net/blog/public/schemas/delegation-ns/.requete-dns-deleguation_m.png&quot; /&gt;&lt;/a&gt;

&lt;figcaption&gt;Requête DNS déléguée : le serveur répond d'aller interroger un autre serveur,&lt;br /&gt;
le client transmet la requête à l'autre serveur et obtient l'enregistrement A.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3&gt;En exemple&lt;/h3&gt;

&lt;p&gt;Prenons un exemple de la vie réelle. Sortez votre commande &lt;em&gt;dig&lt;/em&gt;, et examinez le domaine &lt;em&gt;pingveno.net&lt;/em&gt; :&lt;/p&gt;

&lt;pre&gt;
dig +trace pingveno.net @8.8.8.8

pingveno.net.        172800    IN    NS    ns-197-a.gandi.net.
pingveno.net.        172800    IN    NS    ns-150-b.gandi.net.
pingveno.net.        172800    IN    NS    ns-153-c.gandi.net.
;; Received 733 bytes from 192.52.178.30#53(k.gtld-servers.net) in 46 ms

pingveno.net.        3600    IN    A    xx.xx.xx.xx
;; Received 57 bytes from 217.70.187.154#53(ns-153-c.gandi.net) in 45 ms&lt;/pre&gt;

&lt;p&gt;Dans l'exemple ci-dessus, j'ai retiré les appels du &lt;em&gt;root server&lt;/em&gt; car ils ne sont pas pertinents pour notre exemple. On constate que la requête est transmise aux serveurs de Gandi, qui répondent directement avec l'enregistrement A du domaine.&lt;/p&gt;

&lt;p&gt;Nouvel exemple, avec le sous-domaine uname.pingveno.net :&lt;/p&gt;

&lt;pre&gt;
dig +trace pingveno.net @8.8.8.8

pingveno.net.        172800    IN    NS    ns-197-a.gandi.net.
pingveno.net.        172800    IN    NS    ns-150-b.gandi.net.
pingveno.net.        172800    IN    NS    ns-153-c.gandi.net.
;; Received 737 bytes from 2001:501:b1f9::30#53(m.gtld-servers.net) in 114 ms

pingveno.net.        3600    IN    A    xx.xx.xx.xx
www.pingveno.net.    10800    IN    CNAME    pingveno.net.
;; Received 75 bytes from 217.70.187.154#53(ns-153-c.gandi.net) in 44 ms&lt;/pre&gt;

&lt;p&gt;Cette fois le serveur répond avec un CNAME vers pingveno.net, c'est la méthode &quot;pas chère&quot; de déléguer le sous-domaine à un autre enregistrement. Ce n'est pas un wildcard, puisque l'enregistrement est seul, mais le résultat aurait été le même avec un wildcard.&lt;/p&gt;

&lt;p&gt;Enfin, essayons avec ishimaru.pingveno.net :&lt;/p&gt;

&lt;pre&gt;
dig +trace ishimaru.pingveno.net @8.8.8.8

pingveno.net.        172800    IN    NS    ns-197-a.gandi.net.
pingveno.net.        172800    IN    NS    ns-150-b.gandi.net.
pingveno.net.        172800    IN    NS    ns-153-c.gandi.net.
;; Received 742 bytes from 192.41.162.30#53(l.gtld-servers.net) in 45 ms

ishimaru.pingveno.net.    10800    IN    NS    a.ns.wellhosted.ch.
ishimaru.pingveno.net.    10800    IN    NS    b.ns.wellhosted.ch.
;; Received 98 bytes from 213.167.230.151#53(ns-150-b.gandi.net) in 44 ms

ishimaru.pingveno.net.    60    IN    A    xx.xx.xx.xx
ishimaru.pingveno.net.    3600    IN    NS    b.ns.wellhosted.ch.
ishimaru.pingveno.net.    3600    IN    NS    a.ns.wellhosted.ch.
;; Received 130 bytes from 2a03:2040:d:121::1#53(a.ns.wellhosted.ch) in 49 ms
&lt;/pre&gt;

&lt;p&gt;Cette fois, le serveur a répondu en deux temps : il a indiqué un enregistrement NS sur le sous-domaine, puis le client (ici la commande &lt;em&gt;dig&lt;/em&gt;) a continué la requête vers les serveurs indiqués dans l'enregistrement NS pour retourner finalement l'adresse IP.&lt;/p&gt;

&lt;p&gt;C'est une délégation NS. Cela signifie que tout le sous-domaine (la zone complète) de ishimaru.pingveno.net n'est pas gérée par &lt;em&gt;gandi.net&lt;/em&gt;, mais par &lt;em&gt;a.ns.wellhosted.ch&lt;/em&gt; . De cette manière, le sous-domaine est totalement indépendant, et peut être utilisé pour tous les usages d'un domaine de niveau supérieur.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;C'est tout, ce court article sert juste à démystifier l'enregistrement NS, qui est très peu souvent utilisé, d'une part par la méconnaissance de la plupart des &quot;webdesigners&quot; se contentant de pointer des sous-domaines en A ou en AAAA, et ensuite parce qu'il faut pouvoir déléguer à un serveur DNS, tous les prestataires ne sont pas équipés pour accueillir un domaine de cette façon.&lt;/p&gt;

&lt;h3&gt;Notes&lt;/h3&gt;

&lt;p&gt;&lt;sup&gt;1&lt;/sup&gt; : L'ensemble, ou presque, le serveur DNS ne portant que la zone sur laquelle il a autorité, tout le travail du client DNS consiste justement à interroger les serveurs DNS en cascade, pour arriver à l'enregistrement qui fait autorité. Pas d'exposé là dessus aujourd'hui, mais vous pouvez &lt;a href=&quot;https://fr.wikipedia.org/wiki/Domain_Name_System#Hi%C3%A9rarchie_du_DNS&quot;&gt;lire la doc&lt;/a&gt;.&lt;/p&gt;</description>
        
              </item>
          <item>
        <title>Utiliser l'Object Storage d'OVH (Openstack Swift) avec Updraft Plus</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2019/02/22/Utiliser-l-Object-Storage-d-OVH-%28Openstack-Swift%29-avec-Updraft-Plus</link>
        <guid isPermaLink="false">urn:md5:b834b91b3bbd441c2572e98d70df4077</guid>
        <pubDate>Tue, 26 Feb 2019 17:48:00 +0100</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Informatique</category>
                          <category>cloud</category>
                  <category>openstack</category>
                  <category>ovh</category>
                  <category>storage</category>
                  <category>swift</category>
                  <category>wordpress</category>
                <description>&lt;p&gt;J'ai expérimenté cette semaine l'&lt;a href=&quot;https://www.ovh.com/fr/public-cloud/storage/object-storage/&quot; hreflang=&quot;fr&quot;&gt;Object Storage d'OVH&lt;/a&gt; avec le module de sauvegarde de Wordpress &lt;a href=&quot;https://updraftplus.com/&quot; hreflang=&quot;en&quot;&gt;UpdraftPlus&lt;/a&gt;, et le moins que l'on puisse dire c'est qu'on n'est pas aidé : pas de documentation de A à Z, les noms de configuration qui souffrent à la traduction, bref.&lt;/p&gt;

&lt;p&gt;Voilà comment faire, simplement et du premier coup.&lt;/p&gt;          &lt;p&gt;Créer un Projet dans le manager OVH&lt;/p&gt;

&lt;p&gt;Aller sur le manager OVH, choisir &quot;Cloud&quot; en haut, accepter les contrats, et créer un projet :&lt;/p&gt;

&lt;p&gt;&lt;a class=&quot;media-link&quot; href=&quot;https://uname.pingveno.net/blog/public/captures/ovh-cloud/creer_projet.png&quot;&gt;&lt;img alt=&quot;&quot; class=&quot;media&quot; src=&quot;https://uname.pingveno.net/blog/public/captures/ovh-cloud/.creer_projet_m.png&quot; style=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dans l'onglet &quot;Stockage&quot;, cliquer sur &quot;Créer un conteneur&quot; :&lt;/p&gt;

&lt;p&gt;&lt;a class=&quot;media-link&quot; href=&quot;https://uname.pingveno.net/blog/public/captures/ovh-cloud/creer_conteneur.png&quot;&gt;&lt;img alt=&quot;&quot; class=&quot;media&quot; src=&quot;https://uname.pingveno.net/blog/public/captures/ovh-cloud/.creer_conteneur_m.png&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choisir la zone et le stockage, donner un nom au conteneur.&lt;/p&gt;

&lt;p&gt;Attention, le stockage à froid à bas prix, bien que plus économique sur le prix du stockage, facture les données en entrée, contrairement au conteneur &quot;privé&quot;.&lt;/p&gt;

&lt;p&gt;&lt;a class=&quot;media-link&quot; href=&quot;https://uname.pingveno.net/blog/public/captures/ovh-cloud/creer_container_zone.png&quot;&gt;&lt;img alt=&quot;&quot; class=&quot;media&quot; src=&quot;https://uname.pingveno.net/blog/public/captures/ovh-cloud/.creer_container_zone_m.png&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Aller ensuite dans l'onglet &quot;Gestion technique&quot;, puis &quot;Openstack Users&quot; et choisir &quot;Ajouter un utilisateur&quot; :&lt;/p&gt;

&lt;p&gt;&lt;a class=&quot;media-link&quot; href=&quot;https://uname.pingveno.net/blog/public/captures/ovh-cloud/ovh_cloud_ajouter_utilisateur.png&quot;&gt;&lt;img alt=&quot;&quot; class=&quot;media&quot; src=&quot;https://uname.pingveno.net/blog/public/captures/ovh-cloud/.ovh_cloud_ajouter_utilisateur_m.png&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En face du nom d'utilisateur, ouvrez le menu secondaire, puis choisissez &quot;Ouvrir Openstack Horizon&quot; pour tester&amp;nbsp;les identifiants :&lt;/p&gt;

&lt;p&gt;&lt;a class=&quot;media-link&quot; href=&quot;https://uname.pingveno.net/blog/public/captures/ovh-cloud/ovh_cloud_user_action.png&quot;&gt;&lt;img alt=&quot;&quot; class=&quot;media&quot; src=&quot;https://uname.pingveno.net/blog/public/captures/ovh-cloud/ovh_cloud_user_action.png&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Téléchargez ensuite le fichier de configuration OpenStack et&amp;nbsp;ouvrez-le avec un éditeur de texte.&lt;/p&gt;

&lt;p&gt;Vous êtes prêt à configurer UpdraftPlus. Ouvrez les paramètres d'UpdraftPlus et choisissez &quot;OpenStack (Swift)&quot;&amp;nbsp;:&lt;/p&gt;

&lt;p&gt;&lt;a class=&quot;media-link&quot; href=&quot;https://uname.pingveno.net/blog/public/captures/ovh-cloud/parametrage_updraft_openstack.png&quot;&gt;&lt;img alt=&quot;&quot; class=&quot;media&quot; src=&quot;https://uname.pingveno.net/blog/public/captures/ovh-cloud/.parametrage_updraft_openstack_m.png&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dans &quot;&lt;strong&gt;URI d’authentification&lt;/strong&gt;&quot;, renseignez le contenu de&amp;nbsp;&lt;strong&gt;OS_AUTH_URL&lt;/strong&gt; :&amp;nbsp;https://auth.cloud.ovh.net/v2.0/&lt;/p&gt;

&lt;p&gt;Dans &quot;&lt;strong&gt;Projet&lt;/strong&gt;&quot;, renseignez le contenu de&amp;nbsp;&lt;strong&gt;OS_TENANT_NAME&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dans &quot;&lt;strong&gt;Région&lt;/strong&gt;&quot;, renseignez la&amp;nbsp;région que vous aviez choisi (SBG5, GRA, etc)&lt;/p&gt;

&lt;p&gt;Dans &quot;&lt;strong&gt;Identifiant&lt;/strong&gt;&quot;, renseignez l'identifiant que vous avez créé dans le manager (attention aux espaces en faisant copier-coller)&lt;/p&gt;

&lt;p&gt;Dans &quot;&lt;strong&gt;Mot de passe&lt;/strong&gt;&quot;, renseignez le mot de passe de l'identifiant que vous avez créé dans le manager (attention aux espaces en faisant copier-coller)&lt;/p&gt;

&lt;p&gt;Dans &quot;&lt;strong&gt;Contenant&lt;/strong&gt;&quot;, renseignez le nom de votre containeur (dans l'exemple, mon_containeur).&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Testez la connexion, si ça marche, la sauvegarde vers Openstack est configurée.&lt;/p&gt;</description>
        
              </item>
          <item>
        <title>Je suis hype, j'ai un Tipeee</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2017/11/03/Je-suis-hype%2C-j-ai-un-Tipeee</link>
        <guid isPermaLink="false">urn:md5:883283c01c28a36a9a9603202aa565f0</guid>
        <pubDate>Fri, 03 Nov 2017 13:01:00 +0100</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Website's life</category>
                        <description>          &lt;p&gt;Voilà, c'est fait.&lt;/p&gt;

&lt;p&gt;Presque deux ans après avoir retiré Flattr et ses &lt;a href=&quot;https://uname.pingveno.net/blog/index.php/post/2015/12/06/Nettoyage-estival-%28ou-pas%29&quot;&gt;bénéfices mirobolants&lt;/a&gt;, profitant d'un rhume qui verrouille mon cerveau en mode &quot;lecture seule&quot; depuis deux jours, j'ai fini par &lt;a href=&quot;https://page42.org/auteurs-autrices-il-est-temps-de-creer-votre-tipeee/&quot;&gt;me laisser convaincre&lt;/a&gt; à &lt;a href=&quot;https://www.tipeee.com/pingveno-net&quot;&gt;créer un Tipeee&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vous trouverez donc sous chaque article un petit rappel que oui c'est possible de donner un euro pour remercier le type qui vous a fait gagner 20 minutes, qui vous a fait sourire, ou que vous trouvez simplement &lt;a href=&quot;https://fr.wikipedia.org/wiki/Syst%C3%A8me_nerveux_sympathique&quot;&gt;sympathique&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;À très bientôt sur le blog &lt;img src=&quot;/blog/themes/mathedit_material3/smilies/smile.png&quot; alt=&quot;:)&quot; class=&quot;smiley&quot;&gt;&lt;/p&gt;</description>
        
              </item>
          <item>
        <title>Setup Letsencrypt certificates on Gitlab and Mattermost</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2017/10/19/Setup-Letsencrypt-certificates-on-Gitlab-and-Mattermost</link>
        <guid isPermaLink="false">urn:md5:6269a356f381138560626142d150fc6c</guid>
        <pubDate>Thu, 19 Oct 2017 13:01:00 +0200</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Hacks</category>
                        <description>          &lt;p&gt;The new versions of Gitlab are embedding the Mattermost server. Here is how to setup the certificates for these instances.&lt;/p&gt;

&lt;h3&gt;1. Install certbot&lt;/h3&gt;

&lt;p&gt;Read the docs here (choose Nginx on the appropriate system) : &lt;a href=&quot;https://certbot.eff.org/&quot;&gt;certbot.eff.org&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;2. Make a webroot&lt;/h3&gt;

&lt;pre&gt;
mkdir -p /var/www/letsencrypt/.well-known&lt;/pre&gt;

&lt;h3&gt;3. Configure Gitlab and Mattermost to answer /.well-known to this webroot&lt;/h3&gt;

&lt;p&gt;Edit &lt;strong&gt;/etc/gitlab/gitlab.rb&lt;/strong&gt; and add :&lt;/p&gt;

&lt;pre&gt;
nginx['custom_gitlab_server_config']=&quot;location ^~ /.well-known/ {\n alias /var/www/letsencrypt/.well-known/;\n}\n&quot;
mattermost_nginx['custom_gitlab_mattermost_server_config']=&quot;location /.well-known/ {\n alias /var/www/letsencrypt/.well-known/;\n}\n&quot;&lt;/pre&gt;

&lt;p&gt;And reconfigure Gitlab :&lt;/p&gt;

&lt;pre&gt;
gitlab-ctl reconfigure&lt;/pre&gt;

&lt;h3&gt;4. Create the certificates&lt;/h3&gt;

&lt;p&gt;Run the certbot command :&lt;/p&gt;

&lt;pre&gt;
certbot certonly --staging --webroot --webroot-path=/var/www/letsencrypt/ -d gitlab.your-domain.com
certbot certonly --staging --webroot --webroot-path=/var/www/letsencrypt/ -d mattermost.your-domain.com&lt;/pre&gt;

&lt;h3&gt;5. Tell Gitlab to use the certificates&lt;/h3&gt;

&lt;p&gt;Edit &lt;strong&gt;/etc/gitlab/gitlab.rb&lt;/strong&gt; again, and add :&lt;/p&gt;

&lt;pre&gt;
nginx['redirect_http_to_https'] = true
nginx['ssl_certificate']= &quot;/etc/letsencrypt/live/gitlab.your-domain.com/fullchain.pem&quot;
nginx['ssl_certificate_key'] = &quot;/etc/letsencrypt/live/gitlab.your-domain.com/privkey.pem&quot;

mattermost_nginx['redirect_http_to_https'] = true
mattermost_nginx['ssl_certificate'] = &quot;/etc/letsencrypt/live/mattermost.your-domain.com/fullchain.pem&quot;
mattermost_nginx['ssl_certificate_key'] = &quot;/etc/letsencrypt/live/mattermost.your-domain.com/privkey.pem&quot;
&lt;/pre&gt;

&lt;h3&gt;6. Done&lt;/h3&gt;

&lt;p&gt;Reconfigure Gitlab&amp;nbsp;again :&lt;/p&gt;

&lt;pre&gt;
gitlab-ctl reconfigure&lt;/pre&gt;</description>
        
              </item>
          <item>
        <title>Elasticsearch, Kibana : mapper [hits] cannot be changed from type [long] to [integer]</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2017/10/10/Elastisearch%2C-Kibana-%3A-mapper-%5Bhits%5D-cannot-be-changed-from-type-%5Blong%5D-to-%5Binteger%5D</link>
        <guid isPermaLink="false">urn:md5:c909928e3c21da586381ca95ccee3c3a</guid>
        <pubDate>Tue, 10 Oct 2017 23:34:00 +0200</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Hacks</category>
                        <description>&lt;p&gt;Every time I upgrade my ELK stack, it breaks. This time, it was the Kibana index with this obscurous errors :&lt;/p&gt;

&lt;pre&gt;
[DEBUG][o.e.a.a.i.m.p.TransportPutMappingAction] [logs] failed to put mappings on indices [[[.kibana]]], type [timelion-sheet]
java.lang.IllegalArgumentException: mapper [hits] cannot be changed from type [long] to [integer][DEBUG][o.e.a.a.i.m.p.TransportPutMappingAction]

[DEBUG][o.e.a.a.i.m.p.TransportPutMappingAction] [logs] failed to put mappings on indices [[[.kibana]]], type [timelion-sheet]
java.lang.IllegalArgumentException: mapper [version] cannot be changed from type [long] to [integer]&lt;/pre&gt;

&lt;p&gt;Here is how to fix it. You will have to re-create an index with the correct mapping, and then reindex it.&lt;/p&gt;          &lt;p&gt;First, export your Kibana index mapping and settings :&lt;/p&gt;

&lt;pre&gt;
curl localhost:9200/.kibana/_settings?pretty
curl localhost:9200/.kibana/_mapping?pretty&lt;/pre&gt;

&lt;p&gt;Then, Create and index template with your settings and mapping (don't forget to change the type of the offending fields) :&lt;/p&gt;

&lt;pre&gt;
curl -XPUT &quot;http://localhost:9200/_template/kibana&quot; -H 'Content-Type: application/json' -d'
{
    &quot;template&quot;:&quot;.kibana-5.6&quot;,
    &quot;settings&quot;:{
        &quot;number_of_shards&quot;:1
    },
    &quot;mappings&quot;:{
        &quot;dashboard&quot;:{
            &quot;properties&quot;:{
                &quot;description&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;hits&quot;:{
                    &quot;type&quot;:&quot;integer&quot;
                },
                &quot;kibanaSavedObjectMeta&quot;:{
                    &quot;properties&quot;:{
                        &quot;searchSourceJSON&quot;:{
                            &quot;type&quot;:&quot;string&quot;
                        }
                    }
                },
                &quot;optionsJSON&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;panelsJSON&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;timeFrom&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;timeRestore&quot;:{
                    &quot;type&quot;:&quot;boolean&quot;
                },
                &quot;timeTo&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;title&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;uiStateJSON&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;version&quot;:{
                    &quot;type&quot;:&quot;integer&quot;
                }
            }
        },
        &quot;_default_&quot;:{
            &quot;dynamic&quot;:&quot;strict&quot;
        },
        &quot;url&quot;:{
            &quot;dynamic&quot;:&quot;strict&quot;,
            &quot;properties&quot;:{
                &quot;accessCount&quot;:{
                    &quot;type&quot;:&quot;long&quot;
                },
                &quot;accessDate&quot;:{
                    &quot;type&quot;:&quot;date&quot;,
                    &quot;format&quot;:&quot;strict_date_optional_time||epoch_millis&quot;
                },
                &quot;createDate&quot;:{
                    &quot;type&quot;:&quot;date&quot;,
                    &quot;format&quot;:&quot;strict_date_optional_time||epoch_millis&quot;
                },
                &quot;url&quot;:{
                    &quot;type&quot;:&quot;string&quot;,
                    &quot;fields&quot;:{
                        &quot;keyword&quot;:{
                            &quot;type&quot;:&quot;string&quot;,
                            &quot;index&quot;:&quot;not_analyzed&quot;,
                            &quot;ignore_above&quot;:2048
                        }
                    },
                    &quot;fielddata&quot;:false
                }
            }
        },
        &quot;search&quot;:{
            &quot;properties&quot;:{
                &quot;columns&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;description&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;hits&quot;:{
                    &quot;type&quot;:&quot;integer&quot;
                },
                &quot;kibanaSavedObjectMeta&quot;:{
                    &quot;properties&quot;:{
                        &quot;searchSourceJSON&quot;:{
                            &quot;type&quot;:&quot;string&quot;
                        }
                    }
                },
                &quot;sort&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;title&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;version&quot;:{
                    &quot;type&quot;:&quot;integer&quot;
                }
            }
        },
        &quot;index-pattern&quot;:{
            &quot;properties&quot;:{
                &quot;fieldFormatMap&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;fields&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;timeFieldName&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;title&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                }
            }
        },
        &quot;server&quot;:{
            &quot;dynamic&quot;:&quot;strict&quot;,
            &quot;properties&quot;:{
                &quot;uuid&quot;:{
                    &quot;type&quot;:&quot;string&quot;,
                    &quot;index&quot;:&quot;not_analyzed&quot;
                }
            }
        },
        &quot;config&quot;:{
            &quot;properties&quot;:{
                &quot;buildNum&quot;:{
                    &quot;type&quot;:&quot;string&quot;,
                    &quot;index&quot;:&quot;not_analyzed&quot;
                },
                &quot;defaultIndex&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;discover:aggs:terms:size&quot;:{
                    &quot;type&quot;:&quot;long&quot;
                }
            }
        },
        &quot;visualization&quot;:{
            &quot;properties&quot;:{
                &quot;description&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;kibanaSavedObjectMeta&quot;:{
                    &quot;properties&quot;:{
                        &quot;searchSourceJSON&quot;:{
                            &quot;type&quot;:&quot;string&quot;
                        }
                    }
                },
                &quot;savedSearchId&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;title&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;uiStateJSON&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                },
                &quot;version&quot;:{
                    &quot;type&quot;:&quot;integer&quot;
                },
                &quot;visState&quot;:{
                    &quot;type&quot;:&quot;string&quot;
                }
            }
        }
    }
}
'&lt;/pre&gt;

&lt;p&gt;Then, reindex (copy) the values of your old kibana index to the new one :&lt;/p&gt;

&lt;pre&gt;
curl -XPOST &quot;http://localhost:9200/_reindex&quot; -H 'Content-Type: application/json' -d'
{
  &quot;source&quot;: {
    &quot;index&quot;: &quot;.kibana&quot;
  },
  &quot;dest&quot;: {
    &quot;index&quot;: &quot;.kibana-5.6&quot;
  }
}'&lt;/pre&gt;

&lt;p&gt;And finally, change your kibana index in &lt;strong&gt;/etc/kibana/kibana.yml&lt;/strong&gt; :&lt;/p&gt;

&lt;pre&gt;
kibana.index: &quot;.kibana-5.6&quot;&lt;/pre&gt;

&lt;h3&gt;Sources&lt;/h3&gt;

&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;https://github.com/elastic/kibana/issues/9888&quot; hreflang=&quot;en&quot;&gt;#9888 : mapper [hits] cannot be changed from type [long] to [integer]&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://github.com/elastic/kibana/issues/5648&quot; hreflang=&quot;en&quot;&gt;#5648 : mapper [version] cannot be changed from type [long] to [int] #5648&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
        
              </item>
          <item>
        <title>The war on SPAM: an review of the real world tools</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2017/09/12/The-war-on-SPAM%3A-an-review-of-the-real-world-tools</link>
        <guid isPermaLink="false">urn:md5:61e3fbc7776ac7b0952799741974f844</guid>
        <pubDate>Tue, 12 Sep 2017 02:54:00 +0200</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Informatique</category>
                          <category>blacklist</category>
                  <category>dkim</category>
                  <category>dmarc</category>
                  <category>greylist</category>
                  <category>mail</category>
                  <category>rbl</category>
                  <category>server</category>
                  <category>spam</category>
                  <category>spf</category>
                <description>&lt;p&gt;Anti-spam techniques review: a few hints and tools review from my own experience.&lt;/p&gt;

&lt;p&gt;Spam mesage are very common these days, but filtering them out is not as easy as it seems. The filtering techniques have evolved at the same rate than the spammers' evasive techniques, and the risk of filtering out a legitimate message is greater than ever.&lt;/p&gt;

&lt;p&gt;It is also quite difficult to find good and up-to-date counter-measures list that anyone can implement.&lt;/p&gt;

&lt;p&gt;Here is a summary of the anti-spam strategies I used / am still using. I hope it will help you understanding today's threats, and build your own solutions.&lt;/p&gt;          &lt;h3&gt;Definitions&lt;/h3&gt;

&lt;ul&gt;
	&lt;li&gt;
	&lt;p&gt;&lt;strong&gt;MTA&lt;/strong&gt; : Mail Transport Agent : this is the software that will actually do mail delivery. It is listening on port 25 and answers to SMTP commands. Some common MTAs : Postfix, Exim. &lt;a href=&quot;https://en.wikipedia.org/wiki/Message_transfer_agent&quot;&gt;More about MTA&lt;/a&gt;.&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
	&lt;p&gt;&lt;strong&gt;MX&lt;/strong&gt; : MX records are DNS entries that are identifying the server responsible for mail delivery for the domain. &lt;a href=&quot;https://en.wikipedia.org/wiki/MX_record&quot; hreflang=&quot;en&quot;&gt;More about MX records&lt;/a&gt;.&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
	&lt;p&gt;&lt;strong&gt;RBL&lt;/strong&gt; : Realtime Blackhole List : a list of blacklisted IPs, that should be considered as spam sources. It is called Realtime because they are constantly updated. &lt;a href=&quot;https://en.wikipedia.org/wiki/DNSBL&quot;&gt;More about RBL&lt;/a&gt;.&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
	&lt;p&gt;&lt;strong&gt;RFC&lt;/strong&gt; : Request For Comments, these are proposal for norms, some of them become norms. &lt;a href=&quot;https://en.wikipedia.org/wiki/Request_for_comments&quot;&gt;More about RFC&lt;/a&gt;.&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
	&lt;p&gt;&lt;strong&gt;IPS&lt;/strong&gt; : Intrusion Protection System : these are &quot;smart firewalls&quot; that are blocking malicious requests, often based on behavioral rules. &lt;a href=&quot;https://en.wikipedia.org/wiki/Intrusion_detection_system&quot; hreflang=&quot;en&quot;&gt;More about IPS&lt;/a&gt;.&lt;/p&gt;
	&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;The goals&lt;/h3&gt;

&lt;p&gt;My personal goals on SPAM war are pretty short:&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;
	&lt;p&gt;Minimum false positive: having a spam is better than missing an important mail, try keeping the &quot;permanent bashing&quot; as low as possible&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
	&lt;p&gt;Hit harder on reoffending: coming-back spammers should be slapped harder&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
	&lt;p&gt;Internet neutrality: try not to encourage big mail farm, and let fair little providers doing their business&lt;/p&gt;
	&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Available techniques&lt;/h3&gt;

&lt;h4&gt;Blacklist / Blackhole Lists&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Blacklists&lt;/strong&gt;, &lt;strong&gt;Blackhole Lists&lt;/strong&gt; (or &lt;strong&gt;RBLs&lt;/strong&gt;) are the most ancient and most common measures for reducing spam. They are still pretty accurate, but:&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;It depends A LOT on the &lt;strong&gt;quality of the list&lt;/strong&gt;, trashy lists are very common and they would end up sucking resources for no result, or worse, blocking legitimate emails&lt;/li&gt;
	&lt;li&gt;They are only accurate when &lt;strong&gt;updated often&lt;/strong&gt;. I mean, very often (the R in RBL).&lt;/li&gt;
	&lt;li&gt;You &lt;strong&gt;should not use it directly on the MTA&lt;/strong&gt;, these lists are an &lt;strong&gt;aggressive&lt;/strong&gt; artefact of the past, where spam did not come from mail farms.&lt;/li&gt;
	&lt;li&gt;Very &lt;strong&gt;few are implementing IPv6&lt;/strong&gt;, I agree that IPv6 spam is quite anecdotal, but it will probably change pretty soon (believe me)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Greylist&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Greylisting&lt;/strong&gt; is issuing a temporary &lt;strong&gt;REJECT&lt;/strong&gt; code to force the foreign server to keep the message and send it back later. It aims at increasing the &quot;cost per mail&quot; for spam farms, as they cannot &quot;hit and run&quot; as fast as before.&lt;/p&gt;

&lt;p&gt;The main culprits are that &lt;strong&gt;some providers are not implementing it well&lt;/strong&gt; (hello Facebook) and so it needs an educated whitelist to work properly.&lt;/p&gt;

&lt;p&gt;Some spammers are also re-sending the same mail several times in case of failure, looking like a legitimate mail server, and making &lt;strong&gt;Greylisting&lt;/strong&gt; inefficient.&lt;/p&gt;

&lt;p&gt;It is also hurting the fastness of the mail transmission, as the retry may occur several tens minutes after, &lt;strong&gt;slowing the mail delivery&lt;/strong&gt; with little control on delays.&lt;/p&gt;

&lt;p&gt;Also, some legitimate mail farms (hello OVH) are distributing their retry on several servers, making &lt;strong&gt;Greylisting&lt;/strong&gt; inapplicable without fully whitelisting them.&lt;/p&gt;

&lt;h4&gt;RFC compliance&lt;/h4&gt;

&lt;p&gt;Spammers are often running special softwares for their crafted emails, tightening &lt;strong&gt;RFC compliance&lt;/strong&gt; may be a good way to kick them out.&lt;/p&gt;

&lt;p&gt;It can be as simple as &lt;strong&gt;forcing an HELO on SMTP protocol&lt;/strong&gt;, or more tricky like &lt;strong&gt;checking the mail headers&lt;/strong&gt; or &lt;strong&gt;enforcing a valid reverse&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the majority of cases, it is very efficient, but:&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;Some home-made servers, especially Synology or Windows servers may be blocked while they are sending legitimate email. These servers are often ran by people who doesn't know or care on how to correctly setup a mail server. These buggy setups are more common than you think, and they are often legitimate senders, who have no clue of what is wrong, and are not willing to fix it (did I mentioned banking companies?).&lt;/li&gt;
	&lt;li&gt;In the vast majority of cases, IPv6-ready servers have no reverse on their IPv6 addresses and/or the IPv6 reverse is wrong.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;SPF and DKIM&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;SPF&lt;/strong&gt; and &lt;strong&gt;DKIM&lt;/strong&gt; are anti-spoofing techniques. They does not guarantee that a mail is legitimate, but if the controls are showing an anomaly, it is very likely to be spam (or worse : scam or social engineering tentative).&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;The &lt;strong&gt;SPF&lt;/strong&gt; technique is based on IP or sending domain whitelist: the sending email domain publishes a list of servers allowed to send email, along with a hint on what is expected if it does not pass (soft or hard reject).&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;DKIM&lt;/strong&gt; is much more complex as the sending mail server has to cryptographically sign every message with a domain-specific key, which is then published in a special domain record.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SPF&lt;/strong&gt; has been proven efficient at its beginnings, but today many spammers are just using stolen email accounts or custom domains that does not publish any &lt;strong&gt;SPF&lt;/strong&gt; records, making it less pertinent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DKIM&lt;/strong&gt; is, like &lt;strong&gt;SPF&lt;/strong&gt;, an anti spoofing technique, and spammers may be likely to sign mails from their custom domain if it becomes a necessity. Like &lt;strong&gt;SPF&lt;/strong&gt;, a signed mail is not necessarily a clean mail. By the way, signature problems are pretty comon, and trashing an offending &lt;strong&gt;DKIM&lt;/strong&gt; may not be the right behavior: Yahoo broke a lot of mailing lists when they enforced their &lt;strong&gt;DMARC&lt;/strong&gt; policy.&lt;/p&gt;

&lt;p&gt;To conclude, &lt;strong&gt;DKIM&lt;/strong&gt; is relevant if you need to certify outgoing mails or if you are enforcing policies inside your company (to block spoofed email targeting your organisation) but it is definitely not an efficient anti-spam measure. And the recipient's servers may decide to simply ignore your painfully-configured DKIM headers.&lt;/p&gt;

&lt;h4&gt;Bayesian filters&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Bayesian filters&lt;/strong&gt; are frequency-based spam detection mechanisms. The idea is to sort out ham and spam for a short period of time, to let it &quot;learn&quot; what spam is made of, for efficient content-based detection. It has the benefit of being organisation-specific, as what is ham and what is spam may vary from one company to another (a company selling drugs may not be willing to filter out every message containing the word &quot;pill&quot;).&lt;/p&gt;

&lt;p&gt;But the learning process has to be taken seriously, and many end-users are just deleting spam instead of marking it for feeding the learning. By the way, the learning process needs IMAP folders to sort mails, and it will not work properly if all users are using POP mailboxes.&lt;/p&gt;

&lt;h3&gt;Spammers techniques&lt;/h3&gt;

&lt;p&gt;Or &quot;the today's weapons of this war&quot; .&lt;/p&gt;

&lt;p&gt;Here is a short review of the spammers techniques I know, and some counter measures.&lt;/p&gt;

&lt;h4&gt;Address guessing&lt;/h4&gt;

&lt;p&gt;Some spammers are taking random web domains from their crawling, and then try to send their mails to commonly used addresses patterns. It can be webmaster@ ; ceo@ or whatever. These are easy to spot in log files, and a well configured MTA can take counter measures to lock out these guesses. Free (French ISP) is actually implementing this: &lt;a href=&quot;http://postmaster.free.fr/index_en.html&quot; hreflang=&quot;en&quot;&gt;postmaster.free.fr/index_en.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, some legitimate email are sometimes sent to non existent addresses (typo, user deleted, etc) and legitimate MTA sometimes get blocked if the ban trigger is too low.&lt;/p&gt;

&lt;p&gt;Moreover, nowadays' spammers are distributing their guesses trough zombie machines to stay under triggers, making it hard to spot.&lt;/p&gt;

&lt;h4&gt;Botnets&lt;/h4&gt;

&lt;p&gt;A lot of spammers are using zombies machines to send a high amount of mail in a short amount of time. Commercial ISP are taking the problem seriously, and many are blocking or filtering port 25 on their dynamic ranges, making impossible to have a custom mail server at home, but also preventing infected machines from sending direct-to-SMTP queries.&lt;/p&gt;

&lt;h4&gt;Direct to SMTP connections, ignoring MXes&lt;/h4&gt;

&lt;p&gt;Some spammers are just scanning IP ranges and directly talking to MTA, even if these MTAs are internal and pointed by no MX records. You may think that an authoritarian firewall is the neat solution, but it may be worth collecting these feisty IPs and feed them to an IPS, to protect the network from their guesses on the real MTAs.&lt;/p&gt;

&lt;h4&gt;Fake bounces and backsquatting&lt;/h4&gt;

&lt;p&gt;Sometimes you get an Undelivered Message notice (DSN) for a mail you never sent. This is probably backsquatting.&lt;br /&gt;
Backsquatting is sending email to a buggy address with a valid &quot;from&quot; address. An incorrectly configured mailserver would reply straight away to the from address to notify the failed delivery, instead of rejecting the mail and letting the foreign server doing the dirty job.&lt;/p&gt;

&lt;p&gt;These configuration errors are pretty common, sometimes in defaut configurations or example configurations, but they can be easily avoided (look for documentation about backsquatting for your MTA, and test if your y server is vulnerable).&lt;/p&gt;

&lt;h3&gt;Real life advices&lt;/h3&gt;

&lt;p&gt;I am sorry, I don't have the magic wand to stop all Spam. A good spam fighting solution is always a combination of techniques, SpamAssassin for instance uses scoring from RBL as well as a bayesian filter and SPF checks.&lt;/p&gt;

&lt;p&gt;I think that constant monitoring is important. Not just automated monitoring, but also clever log reading and mind openness on what can be a better solution for each problem. You never know what can happen in a spammer mind, and what works today may not work tomorrow. The Internet of Things is already a game changer.&lt;/p&gt;

&lt;p&gt;I also advise you to be careful. Some decisions on our implementations may really hurt the Internet. Locking whole countries out is not without consequences, and the rise of IPv6 Internet has to be take into consideration from now.&lt;/p&gt;

&lt;p&gt;Things are often not that pretty in mail servers, the temptation is great for a default blocking policy (hello MailInBlack). But as sysadmins, it is our responsability to not abuse and not hurting the smallest actors in the market (mails not coming from big farms). That may be a big word, but in my opinion, the freedom of the Internet also count on our neutrality on mail processing.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;</description>
        
              </item>
          <item>
        <title>Mémo rsync pour mes collègues</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2017/07/17/M%C3%A9mo-rsync-pour-mes-coll%C3%A8gues</link>
        <guid isPermaLink="false">urn:md5:7af6c8e95c4068248d8708cb442e4c70</guid>
        <pubDate>Mon, 17 Jul 2017 15:01:00 +0200</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Hacks</category>
                        <description>&lt;p&gt;Un rappel de l'usage pratique de rsync pour mes collègues qui oublient tout le temps les commandes à taper.&lt;/p&gt;          &lt;h3&gt;Format de base&lt;/h3&gt;

&lt;pre&gt;
rsync arguments source destination&lt;/pre&gt;

&lt;h3&gt;Arguments courants&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;-r&lt;/code&gt; : récursif (copie les dossiers et leur contenu)&lt;br /&gt;
&lt;code&gt;-a&lt;/code&gt; : récursif + conserve permissions et propriétaire (si root)&lt;br /&gt;
&lt;code&gt;-u&lt;/code&gt; : ne pas changer les fichiers plus récents dans la destination&lt;br /&gt;
&lt;code&gt;-v&lt;/code&gt; : verbeux (affiche les fichiers et dossiers affectés)&lt;br /&gt;
&lt;code&gt;-z&lt;/code&gt; : compression du tunnel (pour une connexion fibrée vers des machines lowcost, on peut omettre le paramètre pour soulager le CPU)&lt;br /&gt;
&lt;code&gt;--exclude&lt;/code&gt; : exclus certains chemins d'accès de la synchronisation. Attention, les chemins sont relatifs au chemin la source.&lt;br /&gt;
&lt;code&gt;--dry-run&lt;/code&gt; : montre les opérations sans rien toucher&lt;br /&gt;
&lt;code&gt;--delete&lt;/code&gt; : efface les dossiers supplémentaires de la destination&lt;/p&gt;

&lt;h3&gt;Format de &quot;source&quot; et &quot;destination&quot;&lt;/h3&gt;

&lt;pre&gt;
login@hôte:/chemin/dacces/&lt;/pre&gt;

&lt;p&gt;Attention avec l'utilisation du récursif (activé avec &quot;-r&quot; ou &quot;-a&quot;), &lt;strong&gt;le / à la fin du chemin de la source est important&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;Exemples d'usages corrects&lt;/h3&gt;

&lt;table border=&quot;1&quot; cellpadding=&quot;1&quot; cellspacing=&quot;1&quot;&gt;
	&lt;thead&gt;
		&lt;tr&gt;
			&lt;th scope=&quot;col&quot;&gt;Usage&lt;/th&gt;
			&lt;th scope=&quot;col&quot;&gt;Source&lt;/th&gt;
			&lt;th scope=&quot;col&quot;&gt;Destination&lt;/th&gt;
			&lt;th scope=&quot;col&quot;&gt;Résultat&lt;/th&gt;
			&lt;th scope=&quot;col&quot;&gt;Remarques&lt;/th&gt;
		&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
		&lt;tr&gt;
			&lt;td&gt;Envoyer un fichier dans un dossier&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;/chemin/source/fichier&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;
			&lt;p&gt;/chemin/destination/dossier/&lt;/p&gt;
			&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;/chemin/destination/dossier/fichier&lt;/td&gt;
			&lt;td&gt; &lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;td&gt;Envoyer des fichiers dans un dossier&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;/chemin/source/*&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;
			&lt;p&gt;/chemin/destination/dossier/&lt;/p&gt;
			&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;/chemin/destination/dossier/fichier1&lt;br /&gt;
			/chemin/destination/dossier/fichier2&lt;br /&gt;
			/chemin/destination/dossier/fichier3&lt;br /&gt;
			...&lt;/td&gt;
			&lt;td&gt; &lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;td&gt;Envoyer un dossier dans un dossier&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;/chemin/source/dossier&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;
			&lt;p&gt;/chemin/destination/&lt;/p&gt;
			&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;/chemin/destination/dossier&lt;/td&gt;
			&lt;td&gt;&lt;strong&gt;&lt;span style=&quot;color:#ff0000;&quot;&gt;Ne pas confondre avec la ligne 5 !&lt;/span&gt;&lt;/strong&gt;&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;td&gt;Synchroniser deux fichiers&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;/chemin/source/fichier1&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;/chemin/destination/fichier2&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;/chemin/destination/fichier2&lt;/td&gt;
			&lt;td&gt; &lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;td&gt;Synchroniser deux dossiers&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;/chemin/source/dossier1/&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;/chemin/destination/dossier2/&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;/chemin/destination/dossier2&lt;/td&gt;
			&lt;td&gt;&lt;strong&gt;&lt;span style=&quot;color:#ff0000;&quot;&gt;Ne pas confondre avec la ligne 3 !&lt;/span&gt;&lt;/strong&gt;&lt;/td&gt;
		&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;

&lt;h3&gt;Exemples de mésusages&lt;/h3&gt;

&lt;table border=&quot;1&quot; cellpadding=&quot;1&quot; cellspacing=&quot;1&quot;&gt;
	&lt;thead&gt;
		&lt;tr&gt;
			&lt;th scope=&quot;col&quot;&gt;Mésusage&lt;/th&gt;
			&lt;th scope=&quot;col&quot;&gt;Source&lt;/th&gt;
			&lt;th scope=&quot;col&quot;&gt;Destination&lt;/th&gt;
			&lt;th scope=&quot;col&quot;&gt;Résultat&lt;/th&gt;
			&lt;th scope=&quot;col&quot;&gt;Remarques&lt;/th&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;td&gt;Envoyer un dossier dans un dossier&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;/chemin/source/dossier/&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;/chemin/destination/&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;&lt;span style=&quot;color:#ff0000;&quot;&gt;/chemin/destination/&lt;/span&gt;&lt;/td&gt;
			&lt;td&gt;
			&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color:#ff0000;&quot;&gt;Va synchroniser les deux dossiers au lieu de placer dossier/ dans destination/&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;

			&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color:#ff0000;&quot;&gt;Si des fichiers ont le même nom, ils seront remplacés. Particulièrement dangereux aussi avec --delete&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
			&lt;/td&gt;
		&lt;/tr&gt;
	&lt;/thead&gt;
&lt;/table&gt;

&lt;h3&gt;Exemples d'utilisations correctes&lt;/h3&gt;

&lt;table border=&quot;1&quot; cellpadding=&quot;1&quot; cellspacing=&quot;1&quot;&gt;
	&lt;thead&gt;
		&lt;tr&gt;
			&lt;th scope=&quot;col&quot;&gt;Usage&lt;/th&gt;
			&lt;th scope=&quot;col&quot;&gt;Commande&lt;/th&gt;
			&lt;th scope=&quot;col&quot;&gt;Résultat&lt;/th&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;td&gt;Envoyer un fichier dans un dossier distant&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;rsync --dry-run -azv /source/fichier login@serveur:/destination/dossier/&lt;/td&gt;
			&lt;td&gt;
			&lt;p&gt;Sur &lt;strong&gt;serveur&lt;/strong&gt; :&lt;/p&gt;

			&lt;p&gt;/destination/dossier/fichier&lt;/p&gt;
			&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;td&gt;Envoyer des fichiers dans un dossier distant&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;rsync --dry-run -azv /source/* login@serveur:/destination/dossier/&lt;/td&gt;
			&lt;td&gt;
			&lt;p&gt;Sur &lt;strong&gt;serveur&lt;/strong&gt; :&lt;/p&gt;

			&lt;p&gt;/destination/dossier/fichier1&lt;br /&gt;
			/destination/dossier/fichier2&lt;br /&gt;
			/destination/dossier/fichier3&lt;br /&gt;
			...&lt;/p&gt;
			&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;td&gt;Envoyer des fichiers dans un dossier distant&lt;br /&gt;
			&lt;strong&gt;et efface les autres fichiers contenus dans le dossier de destination&lt;/strong&gt;&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;rsync --dry-run &lt;strong&gt;--delete&lt;/strong&gt; -azv /source/* login@serveur:/destination/dossier/&lt;/td&gt;
			&lt;td&gt;
			&lt;p&gt;Sur &lt;strong&gt;serveur&lt;/strong&gt; :&lt;/p&gt;

			&lt;p&gt;/destination/dossier/fichier1&lt;br /&gt;
			/destination/dossier/fichier2&lt;br /&gt;
			/destination/dossier/fichier3&lt;br /&gt;
			...&lt;/p&gt;
			&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;td&gt;Envoyer un dossier dans un dossier&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;rsync --dry-run -azv /source/dossier login@serveur:/destination/&lt;/td&gt;
			&lt;td&gt;
			&lt;p&gt;Sur &lt;strong&gt;serveur&lt;/strong&gt; :&lt;/p&gt;

			&lt;p&gt;/destination/dossier&lt;/p&gt;
			&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;td&gt;Synchroniser deux fichiers&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;rsync --dry-run -azv /source/fichier1 login@serveur:/source/fichier2&lt;/td&gt;
			&lt;td&gt;
			&lt;p&gt;Sur &lt;strong&gt;serveur&lt;/strong&gt; :&lt;/p&gt;

			&lt;p&gt;/destination/fichier2&lt;/p&gt;
			&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;td&gt;Synchroniser deux dossiers&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;rsync --dry-run -azv /source/dossier1/ login@serveur:/source/dossier2/&lt;/td&gt;
			&lt;td&gt;
			&lt;p&gt;Sur &lt;strong&gt;serveur&lt;/strong&gt; :&lt;/p&gt;

			&lt;p&gt;/destination/dossier2&lt;/p&gt;
			&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;td&gt;Synchroniser deux dossiers&lt;br /&gt;
			&lt;strong&gt;et efface les autres fichiers présents dans le dossier de destination&lt;/strong&gt;&lt;/td&gt;
			&lt;td style=&quot;white-space: nowrap;&quot;&gt;rsync --dry-run &lt;strong&gt;--delete&lt;/strong&gt; -azv /source/dossier1/ login@serveur:/source/dossier2/&lt;/td&gt;
			&lt;td&gt;
			&lt;p&gt;Sur &lt;strong&gt;serveur&lt;/strong&gt; :&lt;/p&gt;

			&lt;p&gt;/destination/dossier2&lt;/p&gt;
			&lt;/td&gt;
		&lt;/tr&gt;
	&lt;/thead&gt;
&lt;/table&gt;
&lt;style type=&quot;text/css&quot;&gt;.post-content table, .post-content tr, .post-content th, .post-content td, .post-excerpt table, .post-excerpt tr, .post-excerpt th, .post-excerpt td { border: solid thin #000; }
.post-content td, .post-excerpt td { padding: 0.5em; }
.post-content th, .post-excerpt th { padding: 0.5em; text-align: center; }
&lt;/style&gt;</description>
        
              </item>
          <item>
        <title>Configure Wordpress for Performance and Stability</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2016/12/08/Configure-Wordpress-for-Performance-and-Stability</link>
        <guid isPermaLink="false">urn:md5:157bb5b206a5971af1189799723bb63b</guid>
        <pubDate>Tue, 11 Jul 2017 10:05:00 +0200</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Hacks</category>
                        <description>&lt;p&gt;Wordpress is a very common CMS nowadays, and works well out-of-the-box. But when it comes to Performance and Security, its default options are not hardening it enough.&lt;/p&gt;

&lt;p&gt;This topic has been discussed a lot on Internet, but here are my tips, as a web developer and sysadmin.&lt;/p&gt;

&lt;h3&gt;Performance : the main culprits&lt;/h3&gt;

&lt;p&gt;On a public websites, the slowness can com from :&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;Network : your provider has as slow network, a huge traffic load, or the user simply has a bad connection&lt;/li&gt;
	&lt;li&gt;Database access : the provider's database may be under load, you query's sizes may be too important, or the access time between the script and the datbase is just &quot;usually slow&quot;.&lt;/li&gt;
	&lt;li&gt;Disk access : the disk where is stored your files may be slow, or does not have proper in-ram caching&lt;/li&gt;
	&lt;li&gt;CPU : the CPU of the machine where you site is hosted may be slow, or you are on a low-cost VPS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no &quot;miracle solution&quot; for a badly designed website, but in the vast majority of cases, we could help a bit with simple solutions.&lt;/p&gt;

&lt;h3&gt;Security : the main threats&lt;/h3&gt;

&lt;p&gt;The main threat in a Wordpress installation is the updates execution. You should update your modules, themes, and core as soon as possible.&lt;/p&gt;

&lt;p&gt;Custom and unmaintained modules and themes can also become a threat as they are not updated anymore, and can contain exploitable leaks.&lt;/p&gt;

&lt;p&gt;There are several way to prevent your site from leaking too many informations on its &quot;healthiness&quot;. It can give you some time to update your website before its exploitation by hackers.&lt;/p&gt;          &lt;h3&gt;Security : Disable XMLRPC&lt;/h3&gt;

&lt;p&gt;Unless you are absolutely sure that you are using it (in particular if you are using Jetpack), disable XMLRPC. XMLRPC is a sort of &quot;remote control&quot; for Wordpress and is widely used as an attack vector for Wordpress : bruteforce, denial of service, scans and other nasty things.&lt;/p&gt;

&lt;p&gt;Save yourself from the unexpected, disable XMLRPC either &lt;a href=&quot;https://fr.wordpress.org/plugins/disable-xml-rpc/&quot;&gt;with a module&lt;/a&gt; or &lt;a href=&quot;https://wordpress.stackexchange.com/questions/219643/best-way-to-eliminate-xmlrpc-php&quot;&gt;web server rules&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Security : Enable Brute Force Protection&lt;/h3&gt;

&lt;p&gt;Brute force is a technique that aims at guessing the administrator password by testing common or stolen passwords. Even if they have little chance to succeeed, they are sucking CPU and network acess for your users.&lt;/p&gt;

&lt;p&gt;Many modules are offering basic brute force protection, please check that your WAF (if you have one) does not provides one already.&lt;/p&gt;

&lt;h3&gt;Performance : Install a cache plugin&lt;/h3&gt;

&lt;p&gt;Caching is very important. It can save you hours of CPU and database access time, and is widely standardized. There are various techniques :&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;Static cache : the web page is stored &quot;as the user sees it&quot;, and is served immediately without executing all the databases queries&lt;/li&gt;
	&lt;li&gt;In-RAM (object) cache : the PHP processor keeps in-memory objects, to load faster. Some assets like images or CSS files can also be cached in-memory by the web server.&lt;/li&gt;
	&lt;li&gt;Browser cache : expiry time can be set for static elements or pages, so that the browser does not query back the file when it is not needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these techniqes are widely known and used in cache plugins. Please use (and configure) one of them.&lt;/p&gt;

&lt;h3&gt;Security : Install a WAF&lt;/h3&gt;

&lt;p&gt;A WAF (Web Application Firewall) is a sort of filter that will prevent your website from being attacked, scanned, or inflitrated by hackers. It is not an absolute protection,&amp;nbsp; but it easily kicks out script kiddies that could scan your website, saving you resources for real users.&lt;/p&gt;

&lt;p&gt;A WAF can be available from your hosting provider, in that case a simple button can enable it. Several modules are also available for Wordpress to do it.&lt;/p&gt;

&lt;p&gt;Beware, these modules are often heavy as they filter every requests, use it combined with a properly configured cache system.&lt;/p&gt;

&lt;h3&gt;Performance : Watch database size and optimize&lt;/h3&gt;

&lt;p&gt;You probably does not need so many content revisions. The revisions are backups of your articles and pages, stored in the database. You can regularly delete older revisions.&lt;/p&gt;

&lt;p&gt;Some modules can do it for you, for instance &lt;a href=&quot;https://fr.wordpress.org/plugins/wp-optimize/&quot;&gt;WP-Optimize&lt;/a&gt;. It is advised to do a backup before executing the purge.&lt;/p&gt;

&lt;p&gt;It should not be a problem, but if your hosting provider does not optimize databases automatically, it can become one.&lt;/p&gt;

&lt;h3&gt;Performance : Use a CDN if possible&lt;/h3&gt;

&lt;p&gt;A CDN is a server designed to give you static content faster that the original site, by using well-located servers, and long retry-times.&lt;/p&gt;

&lt;p&gt;You can have many benefits on using a CDN : caching, faster loading of assets, default compression and optimization, etc.&lt;/p&gt;

&lt;p&gt;If you can afford and configure one, you can really see a performance improvement.&lt;/p&gt;

&lt;h3&gt;Performance : Tweak Apache settings&lt;/h3&gt;

&lt;p&gt;Some actions can be taken if you have access to Apache configuration :&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;Enable gzip compression : gzip compression can be enabled on top of Apache configuration, to compress assets and pages and improve the network overload.&lt;/li&gt;
	&lt;li&gt;Set high expiration duration : you can tell the user's browser to cache the content by setting &lt;a href=&quot;https://wordpress.org/plugins/far-future-expiry-header/&quot; hreflang=&quot;en&quot;&gt;Expiration headers&lt;/a&gt; in your htaccess.&lt;/li&gt;
	&lt;li&gt;Enable Google's mod_pagespeed : Google provides an &lt;a href=&quot;https://developers.google.com/speed/pagespeed/&quot;&gt;auto-optimization module&lt;/a&gt; that can help if your theme is messy. Warning : you must have a good I/O rate in order to use this plugin, it makes many accesses to disks.&lt;/li&gt;
&lt;/ul&gt;</description>
        
              </item>
          <item>
        <title>Cinq raisons qui prouvent que l'IA de Portal est la même que celle de WiiFit</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2015/06/29/Cinq-raisons-qui-prouvent-que-l-IA-de-Portal-est-la-m%C3%AAme-que-celle-de-WiiFit</link>
        <guid isPermaLink="false">urn:md5:f51befc32e10d4493456c682003c5561</guid>
        <pubDate>Wed, 04 Jan 2017 08:30:00 +0100</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Humour</category>
                          <category>humour</category>
                  <category>portal</category>
                  <category>wiifit</category>
                <description>&lt;p&gt;&lt;img alt=&quot;aperture_wiifit.jpg&quot; class=&quot;media&quot; src=&quot;https://uname.pingveno.net/blog/public/portal-wiifit/.aperture_wiifit_m.jpg&quot; style=&quot;margin: 0px auto;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On m'a fait jouer (de force) à Wii Fit il y a quelques jours, et cette fois c'est une certitude : l'IA qui ponctue l'avancée du joueur dans Portal est la même que celle du jeu Wii Fit. Voici pourquoi.&lt;/p&gt;

&lt;p&gt;&lt;span style=&quot;color:#696969;&quot;&gt;Nota Bene : cet article est le produit d'un Programme Expérimental d'Enrichissement du Blog par Recyclage d'Articles Restés en Brouillon (PEEBRARB).&lt;/span&gt;&lt;/p&gt;          &lt;h3&gt;1. La voix est la même&lt;/h3&gt;

&lt;div style=&quot;text-align:center&quot;&gt;
&lt;div style=&quot;float:right; margin: 10px; padding: 5px; display: inline-block; text-align:center; border: solid thin #c0c0c0;&quot;&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;glados.jpg&quot; class=&quot;media&quot; src=&quot;https://uname.pingveno.net/blog/public/portal-wiifit/.glados_m.jpg&quot; /&gt;&lt;br /&gt;
&lt;em&gt;Source : &lt;a href=&quot;http://thebaneofqueequeg.blogspot.fr/2012/04/top-10-game-bosses.html&quot;&gt;http://thebaneofqueequeg.blogspot.fr/2012/04/top-10-game-bosses.html&lt;/a&gt;&lt;/em&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Vous vous souvenez de cette voix métallique, féminine, qui tout en douceur vous assène vos quatre vérités avec une implacable assurance ?&lt;/p&gt;

&lt;p&gt;C'est la voix de GladOS dans Portal, mais c'est aussi celle de la console WiiFit. La douce mélodie de la voix n'étant qu'un prétexte à vous pousser à faire ce qu'elle veut.&lt;/p&gt;

&lt;h3 style=&quot;clear:both;&quot;&gt;2. Elles font des commentaires désobligeants sur votre physique&lt;/h3&gt;

&lt;div style=&quot;margin: 10px; float:right; padding: 5px; display: inline-block; text-aligne:center; border: solid thin #c0c0c0;&quot;&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;overweight.jpg&quot; class=&quot;media&quot; src=&quot;https://uname.pingveno.net/blog/public/portal-wiifit/.overweight_m.jpg&quot; title=&quot;overweight.jpg, août 2015&quot; /&gt;&lt;br /&gt;
&lt;em&gt;Source : &lt;/em&gt;&lt;a href=&quot;http://pressontogether.com/2012/07/10/training-tuesday-obese-no-more/&quot;&gt;http://pressontogether.com/2012/07/10/training-tuesday-obese-no-more/&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;L'une comme l'autre a toujours son petit commentaire ou sa petite phrase pour vous rappeler qu'en fait, si vous jouez à ce jeu c'est que vous aimez vous faire insulter :&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;« &lt;em&gt;Vous êtes déséquilibré !&lt;/em&gt; » [WiiFit]&lt;/li&gt;
	&lt;li&gt;« &lt;em&gt;Bientôt, vous serez si grosse que vous tomberez comme une pierre en sautant. Dans de l'acide. Comme une grosse patate dans une friteuse.&lt;/em&gt; » [GladOS]&lt;/li&gt;
	&lt;li&gt;« &lt;em&gt;Ne bougez plus pendant la mesure. Voici les résultats : vous avez pris du poids !&lt;/em&gt; » [WiiFit]&lt;/li&gt;
	&lt;li&gt;« &lt;em&gt;Hmm. Cette Plaque ne doit pas être étalonnée pour quelqu'un de votre... adiposité. Je vais augmenter le poids maximum de quelques zéros.&lt;/em&gt; » [GladOS]&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 style=&quot;clear:both;&quot;&gt;3. Elles vous font faire des &quot;tests&quot; et &quot;exercices&quot; qui vous semblent inutile&lt;/h3&gt;

&lt;div style=&quot;margin: 10px; float:right; padding: 5px; display: inline-block; text-aligne:center; border: solid thin #c0c0c0;&quot;&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;exercise.jpg&quot; class=&quot;media&quot; src=&quot;https://uname.pingveno.net/blog/public/portal-wiifit/exercise.jpg&quot; title=&quot;exercise.jpg, août 2015&quot; /&gt;&lt;br /&gt;
&lt;em&gt;Oui, c'est comme ça qu'on fait !&lt;br /&gt;
Source : &lt;/em&gt;&lt;a href=&quot;http://littlefatgirl.blogspot.fr/2011/01/my-morning-workout.html&quot;&gt;http://littlefatgirl.blogspot.fr/2011/01/my-morning-workout.html&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Que ça soit la séance insoutenable d'abdos fessiers, les positions improbables de yoga ou les salles de test avec des tourelles et des lasers, les deux IA excellent dans l'art de vous confronter à des situations inconnues, voire dangereuses. Elles vous encouragent juste le nécessaire pour vous faire passer au test suivant, et les exercices recommencent avec de plus en plus de difficulté.&lt;/p&gt;

&lt;p&gt;Pour ceux qui ont encore des doutes : est-ce que vous avez la possibilité de « terminer le jeu » en « battant le boss » avec WiiFit ? Non ? C'est bien la preuve que cette IA aussi est accro à l'&lt;a href=&quot;http://theportalwiki.com/wiki/Perpetual_Testing_Initiative/fr&quot;&gt;initiative de test perpétuelle&lt;/a&gt;.&lt;/p&gt;

&lt;h3 style=&quot;clear:both;&quot;&gt;4. Elles essaient de vous tuer&lt;/h3&gt;

&lt;div style=&quot;margin: 10px; float:right; padding: 5px; display: inline-block; text-aligne:center; border: solid thin #c0c0c0;&quot;&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;turrets.jpg&quot; class=&quot;media&quot; src=&quot;https://uname.pingveno.net/blog/public/portal-wiifit/.turrets_m.jpg&quot; title=&quot;turrets.jpg, août 2015&quot; /&gt;&lt;br /&gt;
&lt;em&gt;« Bonjour. »&lt;/em&gt;&lt;br /&gt;
&lt;em&gt;Source : &lt;a href=&quot;https://www.youtube.com/watch?v=Tg67XggeraM&quot;&gt;https://www.youtube.com/watch?v=Tg67XggeraM&lt;/a&gt;&lt;/em&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;C'est peut-être pas évident pour la WiiFit, mais c'est justement le piège car cette IA est beaucoup plus subtile : vous avez survécue à trois heures d'abdos fessiers, deux heures de ski, et quatre heures de gymnastique rythmique ? Attendez, il y a encore plein d'autres exercices ! C'est bien la preuve que les deux IA partagent la même motivation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;« Le prochain test contient des tourelles. Vous vous en souvenez ? Les choses pâles et froides remplies de balles ? Ah non, ça c'est vous dans cinq secondes. Bonne chance. » [GladOS]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 style=&quot;clear:both;&quot;&gt;5. L'entraineuse WiiFit ressemble étrangement à Chell&lt;/h3&gt;

&lt;p&gt;Il y a vraiment une ressemblance, non ?&lt;/p&gt;

&lt;div style=&quot;text-align:center&quot;&gt;
&lt;div style=&quot;margin: 10px; padding: 5px; display: inline-block; text-align:center; border: solid thin #c0c0c0;&quot;&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;chell.jpg&quot; class=&quot;media&quot; src=&quot;https://uname.pingveno.net/blog/public/portal-wiifit/.chell_m.jpg&quot; style=&quot;text-align:center;&quot; /&gt;&lt;br /&gt;
&lt;em&gt;Source : &lt;a href=&quot;https://www.flickr.com/photos/64097747@N05/5837262217&quot;&gt;https://www.flickr.com/photos/64097747@N05/5837262217&lt;/a&gt;&lt;/em&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div style=&quot;margin: 10px; padding: 5px; display: inline-block; text-align:center; border: solid thin #c0c0c0;&quot;&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;wiifit_trainer.png&quot; class=&quot;media&quot; src=&quot;https://uname.pingveno.net/blog/public/portal-wiifit/.wiifit_trainer_m.png&quot; title=&quot;wiifit_trainer.png, août 2015&quot; /&gt;&lt;br /&gt;
&lt;em&gt;Source : &lt;a href=&quot;http://www.ssbwiki.com/Wii_Fit_Trainer&quot;&gt;http://www.ssbwiki.com/Wii_Fit_Trainer&lt;/a&gt;&lt;/em&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Je n'explique pas cette singularité pour l'instant, mais les théories les plus sérieuses sont les bienvenues dans les commentaires ! &lt;strong&gt;Coincidences ? Je ne pense pas.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;/ul&gt;</description>
        
              </item>
          <item>
        <title>Sample public calendar for ownCloud using ICS parser</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2016/07/30/Sample-public-calendar-for-ownCloud-using-ICS-parser</link>
        <guid isPermaLink="false">urn:md5:f4a988fe3fc58cd4cb73d840ed3068f5</guid>
        <pubDate>Sat, 30 Jul 2016 08:09:00 +0200</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Hacks</category>
                        <description>&lt;p&gt;When ownCloud removed the ability to share a calendar publicly, I had no other choice than forcing my acquaintances to register to my ownCloud.&lt;/p&gt;

&lt;p&gt;I didn't want that, so I implemented my own solution.&lt;/p&gt;          &lt;h3&gt;1. Abstract&lt;/h3&gt;

&lt;p&gt;My calendar works like this :&lt;/p&gt;

&lt;p&gt;I write events in a particular calendar.&lt;/p&gt;

&lt;p&gt;I share this calendar with a special ownCloud user, dedicated for this.&lt;/p&gt;

&lt;p&gt;The script will login on the CalDAV API with the credential of the special user, and get the content of the shared calendar.&lt;/p&gt;

&lt;p&gt;The script compile the events and compile a nice table, with the content of the calendar.&lt;/p&gt;

&lt;p&gt;Please note that the events I write are special: I only use the title field to specify a &quot;color&quot; that will be displayed for the day. The colors are corresponding to my « level of availability » (to show my holidays dates, for instance)&lt;/p&gt;

&lt;h3&gt;2. Create the calendar and the user&lt;/h3&gt;

&lt;p&gt;In owncloud, create the second user. I called it &lt;strong&gt;public&lt;/strong&gt;, for instance.&lt;/p&gt;

&lt;p&gt;In ownCloud, create the shared calendar on your own account, and give &lt;strong&gt;public&lt;/strong&gt; access to it. Do not tick write permissions.&lt;/p&gt;

&lt;p&gt;Create a test event, and log out.&lt;/p&gt;

&lt;p&gt;Log in back with the &lt;strong&gt;public&lt;/strong&gt; user, and check that you see the shared calendar and its content.&lt;/p&gt;

&lt;h3&gt;3. Download the library&lt;/h3&gt;

&lt;p&gt;I used &lt;a href=&quot;https://github.com/johngrogg/ics-parser&quot; hreflang=&quot;en&quot;&gt;this ICS parser&lt;/a&gt; to parse the CalDAV respponses. Download it on your web hosting to the direcory &lt;strong&gt;ics-parser/&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;4. Install the script&lt;/h3&gt;

&lt;p&gt;Here is the script I use:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php
// Require ICS parser library
require_once('ics-parser/class.iCalReader.php');

// Use it to debug, the previously included parser disables the errors
error_reporting(E_ALL);
ini_set('display_errors', '1');

// Simple caching system, feel free to change the delay
if (file_exists('calendar.cache.html')) {
    $last_update = filemtime('calendar.cache.html');
} else {
        $last_update = 0;
}
if ($last_update + 60*60 &amp;lt; time()) {

// Get events

$headers = array(
        'Content-Type: application/xml; charset=utf-8',
        'Depth: 1',
        'Prefer: return-minimal'
    );

// Setup the calendar URL and the credentials here
$calendar_url = 'https://example.com/remote.php/caldav/calendars/public/public_shared_by_YOU';
$calendar_user = 'public';
$calendar_password = 'public_password';

// Prepare request body
$doc  = new DOMDocument('1.0', 'utf-8');
$doc-&amp;gt;formatOutput = true;

$query = $doc-&amp;gt;createElement('c:calendar-query');
$query-&amp;gt;setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:c', 'urn:ietf:params:xml:ns:caldav');
$query-&amp;gt;setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:d', 'DAV:');

$prop = $doc-&amp;gt;createElement('d:prop');
$prop-&amp;gt;appendChild($doc-&amp;gt;createElement('d:getetag'));
$prop-&amp;gt;appendChild($doc-&amp;gt;createElement('c:calendar-data'));
$query-&amp;gt;appendChild($prop);

$prop = $doc-&amp;gt;createElement('c:filter');
$filter = $doc-&amp;gt;createElement('c:comp-filter');
$filter-&amp;gt;setAttribute('name', 'VCALENDAR');
$prop-&amp;gt;appendChild($filter);
$query-&amp;gt;appendChild($prop);

$doc-&amp;gt;appendChild($query);
$body = $doc-&amp;gt;saveXML();

// Debugging purpose
//echo '&amp;lt;pre&amp;gt;' . htmlspecialchars($body) . '&amp;lt;/pre&amp;gt;';

// Prepare cURL request
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $calendar_url);
curl_setopt($ch, CURLOPT_USERPWD, $calendar_user . ':' . $calendar_password);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'REPORT');
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);

$response = curl_exec($ch);
if (curl_error($ch)) {
    //echo curl_error($ch);
    exit();
}
curl_close($ch);

// Debugging purpose
//echo '&amp;lt;pre&amp;gt;' . htmlspecialchars($response) . '&amp;lt;/pre&amp;gt;';

// Get the useful part of the response
$xml = simplexml_load_string($response);
$data = $xml-&amp;gt;xpath('//cal:calendar-data');

// Debugging purpose
//echo '&amp;lt;pre&amp;gt;' . htmlspecialchars($data[0]) . '&amp;lt;/pre&amp;gt;';

// Parse events
$calendar_events = array();
foreach ($data as $vcalendars) {
    $ical = new ICal();
    $vcalendars = $vcalendars-&amp;gt;__tostring();

    $lines = explode(&quot;\n&quot;, $vcalendars);
    $ical-&amp;gt;initLines($lines);
    $events = $ical-&amp;gt;events();

    foreach ($events as $event) {
        $start = $ical-&amp;gt;iCalDateToUnixTimestamp($event['DTSTART']);
        $end = $ical-&amp;gt;iCalDateToUnixTimestamp($event['DTEND']);
        $summary = $event['SUMMARY'];
        $calendar_events[] = array(
            'start' =&amp;gt; $start,
            'end' =&amp;gt; $end,
            'summary' =&amp;gt; $summary,
        );
    }
}

// Function to sort the events by start date
function _sort_events($a, $b) {
    return ($a['start'] &amp;gt; $b['start']);
}

// Sort the events by start date
// By doing so, you can have overlapping events, the newest event will superseed the previous
usort($calendar_events, '_sort_events');

// Function to convert my &quot;special titles&quot; into hex colors
function color2hex($color) {
    $color = strtolower($color);

    switch($color) {
        case 'green':
        return '#00ff00';
        break;

        case 'orange':
        return '#ffaa00';
        break;

        case 'red':
        return '#ff0000':
        break;

        case 'black':
        return '#000000';
        break;

        case 'blue':
        return '#0000ff';
        break;
    }
}

// Bufferize output for caching system
ob_start();

// Get current date
$now = mktime(0,0,0);

// Initiate the color array
// Each day in our calendar table will have a color
// Each color is an hex color, corresponding tu the event title (see the switch-case block upper)
$day_color = array();

// Loop through ALL events (old events included)
foreach ($calendar_events as $event) {

    // Discard passed events
    if ($event['end'] &amp;lt; time()) {
        continue;
    }

    // Calculate the timestamp of the &quot;start&quot; date of the event
    $current_day = mktime(0,0,0, date('n', $event['start']), date('j', $event['start']), date('Y', $event['start']));
    $first_loop = true;
    // Loop to fill the color array from the event start date to the event end date
    for ($d = $current_day; ($first_loop || $d &amp;lt; $event['end']); $d+=(60*60*24)) {

        // Debugging purpose
        //var_dump(date('r', $current_day) . ' ' . date('r', $d) . ' ' . date('r', $event['start']) . ' ' . date('r', $event['end']));

        // Handle overlapping events: stack color at the beginning of the sub-array
        if (isset($day_color[$d]) &amp;amp;&amp;amp; is_array($day_color[$d])) {
            array_unshift($day_color[$d], color2hex($event['summary']));
        } else {
            $day_color[$d][0] = color2hex($event['summary']);
        }

        $first_loop = false;
    }
}

// Debugging purpose
//var_dump($day_color);

// Output the final table
/ My week starts on Monday, but you can change it

$week_day = date('N', $now);

echo '&amp;lt;table class=&quot;forecast&quot;&amp;gt;';
echo '&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Mon&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Tue&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Wed&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Thu&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Fri&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Sat&amp;lt;/th&amp;gt;&amp;lt;tH&amp;gt;Sun&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;';

// Loop through the color array
for ($row = 0; $row &amp;lt; 10; $row++) {
    echo '&amp;lt;tr&amp;gt;';

    echo '&amp;lt;td class=&quot;week&quot;&amp;gt;' . (date('W', $now) + $row) . '&amp;lt;/td&amp;gt;';
    for ($col = 0; $col &amp;lt; 7; $col++) {

        // Skip past days from the first week (leave it blank)
        if ($row == 0 &amp;amp;&amp;amp; $col &amp;lt; $week_day-1) {
            echo '&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;';
            continue;
        }

        $current_day = $now + ($row)*(7*60*60*24) + ($col-($week_day-1))*(60*60*24);
        $current_day = mktime(0,0,0, date('n', $current_day), date('j', $current_day), date('Y', $current_day)); // Working around daylight saving time
        if (isset($day_color[$current_day]) &amp;amp;&amp;amp; isset($day_color[$current_day][0])) {
            // We choose to get the first color of the array,
            //   but if you want to display overlapping events colors,
            //   you can use the other values of the array
            $color = $day_color[$current_day][0];
            echo '&amp;lt;td class=&quot;' . $color . '&quot;&amp;gt;';
        } else {
            echo '&amp;lt;td&amp;gt;';
        }
        // Debugging purpose
        //echo '&amp;lt;pre&amp;gt;' . $current_day . '&amp;lt;/pre&amp;gt;';
        //echo date('r', $current_day);
        // Label the cell with the current day
        echo date('j', $current_day) . ' ' . date('M', $current_day) . ' ' . date('Y', $current_day);
        echo '&amp;lt;/td&amp;gt;';
    }
    echo '&amp;lt;/tr&amp;gt;';
}

echo '&amp;lt;/table&amp;gt;';

$html = ob_get_clean();
ob_end_flush();

file_put_contents('calendar.cache.html', $html);

} else {
    $html = file_get_contents('calendar.cache.html');
}

echo $html;&lt;/pre&gt;

&lt;p&gt;Copy and paste it to an empty PHP file.&lt;/p&gt;

&lt;p&gt;Please note that it has a small caching system (calendar.cache.html) to prevent fetching events every time someone loads the page. You can easily reduce the delay or deactivate this feature.&lt;/p&gt;

&lt;p&gt;Refer to the comments to extend it.&lt;/p&gt;</description>
        
              </item>
          <item>
        <title>Drupal 8 : create a custom Rule Action</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2016/07/27/Drupal-8-%3A-create-a-custom-Rule-action</link>
        <guid isPermaLink="false">urn:md5:4028da17989c185a5f86e2d3f8a25968</guid>
        <pubDate>Wed, 27 Jul 2016 10:56:00 +0200</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Hacks</category>
                          <category>action</category>
                  <category>drupal</category>
                  <category>drupal 8</category>
                  <category>module</category>
                  <category>php</category>
                  <category>rules</category>
                <description>          &lt;p&gt;Warning : the Rule plugin for Drupal 8 was not considered stable when I wrote this post.&lt;/p&gt;

&lt;h3&gt;1. Drupal 8: the new modules paradigm&lt;/h3&gt;

&lt;p&gt;Drupal 8 modules structure changed, and many modules are now using the new API.&lt;/p&gt;

&lt;p&gt;No more plain old functions name like hook_something, no more obscurous PHP for menu entries. Instead, Object Oriented programming, YAML configuration files, magic comments, and Symfony routing.&lt;/p&gt;

&lt;p&gt;Let's give it a try, let's make our first simplest module.&lt;/p&gt;

&lt;h3&gt;2. Base module configuration&lt;/h3&gt;

&lt;p&gt;If you already know how to do this, jumb to next section.&lt;/p&gt;

&lt;p&gt;Create a directory for your module, for instance &lt;strong&gt;mymodule&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Create an info file in YAML format : &lt;strong&gt;mymodule/mymodule.info.yaml&lt;/strong&gt;:&lt;/p&gt;

&lt;pre&gt;
name: 'My Module'
type: module
core: 8.x
package: Custom&lt;/pre&gt;

&lt;p&gt;You don't need more! You don't need routing as you will plug into Rules.&lt;/p&gt;

&lt;h3&gt;3. File names and Namespaces&lt;/h3&gt;

&lt;p&gt;Create the &lt;strong&gt;RulesAction&lt;/strong&gt; directory and its parents: &lt;strong&gt;mymodule/src/Plugin/RulesAction/&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a PHP file for your class: &lt;strong&gt;MyAction.php&lt;/strong&gt;. The class will be loaded with the autoload system.&lt;/p&gt;

&lt;p&gt;Edit the file you just created, and specify the namespace at the beginning of the file:&lt;/p&gt;

&lt;pre&gt;
/**
 * @file
 * Contains \Drupal\mymodule\Plugin\RulesAction\MyAction.
 */
namespace Drupal\mymodule\Plugin\RulesAction;
use Drupal\rules\Core\RulesActionBase;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;The namespace must correspond to your module name and class name!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;4. Rule Action specifications and parameters&lt;/h3&gt;

&lt;p&gt;Comments are very important as they are also specifications. You will define your Action id, description, and parameters. For instance, here is a copied sample from the predefined action DataSet:&lt;/p&gt;

&lt;pre&gt;
/**
 * Provides a 'My action' action.
 *
 * @RulesAction(
 *   id = &quot;rules_myaction&quot;,
 *   label = @Translation(&quot;Set a data value&quot;),
 *   category = @Translation(&quot;Data&quot;),
 *   context = {
 *     &quot;data&quot; = @ContextDefinition(&quot;any&quot;,
 *       label = @Translation(&quot;Data&quot;),
 *       description = @Translation(&quot;Specifies the data to be modified using a data selector, e.g. 'node:author:name'.&quot;),
 *       allow_null = TRUE,
 *       assignment_restriction = &quot;selector&quot;
 *     ),
 *     &quot;value&quot; = @ContextDefinition(&quot;any&quot;,
 *       label = @Translation(&quot;Value&quot;),
 *       description = @Translation(&quot;The new value to set for the specified data.&quot;),
 *       default_value = NULL,
 *       required = FALSE
 *     )
 *   }
 * )
 */
&lt;/pre&gt;

&lt;p&gt;The Rule id is &lt;strong&gt;rules_myaction&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The category of the action in the dropdown menu is &lt;strong&gt;Data&lt;/strong&gt; and it will be labeled &lt;strong&gt;Set a data value&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The current action will have two parameters : the data that will be changed, and the corresponding value.&lt;/p&gt;

&lt;h3&gt;5. Rule Action callback&lt;/h3&gt;

&lt;p&gt;Again, no more plain callback, the function &lt;strong&gt;doExecute&lt;/strong&gt; will be called to execute your Action:&lt;/p&gt;

&lt;pre&gt;
class MyAction extends RulesActionBase {

  /**
   * Executes the Plugin.
   *
   * @param mixed $data
   *   Original value of an element which is being updated.
   * @param mixed $value
   *   A new value which is being set to an element identified by data selector.
   */
  protected function doExecute($data, $value) {
    $typed_data = $this-&amp;gt;getContext('data')-&amp;gt;getContextData();
    $typed_data-&amp;gt;setValue($value);
  }

  /**
   * {@inheritdoc}
   */
  public function autoSaveContext() {
    // Saving is done at the root of the typed data tree, for example on the
    // entity level.
    $typed_data = $this-&amp;gt;getContext('data')-&amp;gt;getContextData();
    $root = $typed_data-&amp;gt;getRoot();
    $value = $root-&amp;gt;getValue();
    // Only save things that are objects and have a save() method.
    if (is_object($value) &amp;amp;&amp;amp; method_exists($value, 'save')) {
      return ['data'];
    }
    return [];
  }

}&lt;/pre&gt;

&lt;p&gt;The class name has to correspond to your file name.&lt;/p&gt;

&lt;p&gt;In this sample, the parameter &lt;strong&gt;$value&lt;/strong&gt; will be set to &lt;strong&gt;$data&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;6. Troubleshooting&lt;/h3&gt;

&lt;pre&gt;
Uncaught PHP Exception Drupal\\Component\\Plugin\\Exception\\PluginException: &quot;Plugin (rules_myaction) instance class &quot;Drupal\\tbh_system\\Plugin\\RulesAction\\MyAction&quot; does not exist.&quot;&lt;/pre&gt;

&lt;p&gt;Your namespace/classname/filename/directoryname is probably wrong.&lt;/p&gt;

&lt;h3&gt;Source&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://fago.gitbooks.io/rules-docs/content/extending_rules/rules_action_plugins.html&quot; hreflang=&quot;en&quot;&gt;Drupal 8 Rules Action documentation&lt;/a&gt;&lt;/p&gt;</description>
        
              </item>
          <item>
        <title>Debian 8 : Configure Nginx and Passenger to supercharge your PuppetMaster</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2016/06/22/Debian-8-%3A-Configure-Nginx-and-Passenger-to-supercharge-your-PuppetMaster</link>
        <guid isPermaLink="false">urn:md5:67dac3b40d4c262e3ea9a071eb94d2e6</guid>
        <pubDate>Wed, 22 Jun 2016 21:16:00 +0200</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>Hacks</category>
                          <category>debian</category>
                  <category>jessie</category>
                  <category>mongrel</category>
                  <category>nginx</category>
                  <category>passenger</category>
                  <category>puppet</category>
                  <category>puppetmaster</category>
                  <category>ruby</category>
                <description>&lt;p&gt;The &lt;a href=&quot;http://puppetlabs.com/&quot;&gt;Puppet&lt;/a&gt; master comes by default with a basic WEBrick server. It allow a quick start for those that are not familiar with Puppet, but when the number of Puppet nodes grows, the performances of the default WEBrick server are going down quickly.&lt;/p&gt;

&lt;p&gt;The Puppet documentation show how to configure Apache and Passenger to replace the default WEBrick server, but what if you have a lot of nodes ? What if you want to apply your configuration within minutes, instead of the default half-hour threshold before the agent asks the master if something changed ?&lt;/p&gt;

&lt;p&gt;Or you may just want a fancy Nginx instead of your plain-old-reliable Apache.&lt;/p&gt;

&lt;p&gt;Here is how.&lt;/p&gt;          &lt;h3&gt;Check your hostname&lt;/h3&gt;

&lt;p&gt;Your hostname is the base configuration for your node, you should check that it's correct, otherwise you will run into problems after Puppet installation.&lt;/p&gt;

&lt;pre&gt;
# hostname -f&lt;/pre&gt;

&lt;p&gt;If everything is okay, check your hosts file&lt;/p&gt;

&lt;pre&gt;
# cat /etc/hosts&lt;/pre&gt;

&lt;p&gt;If your hostname is inside your host file, carry on. Otherwise, set it.&lt;/p&gt;

&lt;h3&gt;Install Puppet and Puppetmaster&lt;/h3&gt;

&lt;p&gt;I suppose that you also need the puppet agent installed on the Puppetmaster server.&lt;/p&gt;

&lt;p&gt;Install Puppet and Puppetmaster :&lt;/p&gt;

&lt;pre&gt;
# apt-get install puppet puppetmaster&lt;/pre&gt;

&lt;p&gt;Stop the Puppetmaster :&lt;/p&gt;

&lt;pre&gt;
# service puppetmaster stop&lt;/pre&gt;

&lt;p&gt;Prevent the puppetmaster from starting. Nginx will spawn on the right port instead of the WEBrick server, previously spawn by Puppetmaster service. Edit the file &lt;strong&gt;/etc/defaults/puppetmaster&lt;/strong&gt; :&lt;/p&gt;

&lt;pre&gt;
# Start puppetmaster on boot?
START=no&lt;/pre&gt;

&lt;p&gt;Configure the Puppet agent : edit the file &lt;strong&gt;/etc/puppet/puppet.conf&lt;/strong&gt; to point your agent on the master (for instance puppetmaster.example.com).&lt;/p&gt;

&lt;p&gt;Also, &lt;strong&gt;comment&lt;/strong&gt; the two lines that are &quot;needed for passenger&quot;, our configuration don't need them. Actually, &lt;strong&gt;if you keep it, it will not work&lt;/strong&gt;.&lt;/p&gt;

&lt;pre&gt;
[main]
logdir=/var/log/puppet
vardir=/var/lib/puppet
ssldir=/var/lib/puppet/ssl
rundir=/var/run/puppet
factpath=$vardir/lib/facter
prerun_command=/etc/puppet/etckeeper-commit-pre
postrun_command=/etc/puppet/etckeeper-commit-post
&lt;strong&gt;server=puppetmaster.example.com&lt;/strong&gt;

[master]
# These are needed when the puppetmaster is run by passenger
# and can safely be removed if webrick is used.
&lt;strong&gt;#ssl_client_header = SSL_CLIENT_S_DN
#ssl_client_verify_header = SSL_CLIENT_VERIFY&lt;/strong&gt;

[agent]
report = true&lt;/pre&gt;

&lt;p&gt;Enable your puppet agent :&lt;/p&gt;

&lt;pre&gt;
# puppet agent --enable&lt;/pre&gt;

&lt;h3&gt;Install Nginx and Passenger&lt;/h3&gt;

&lt;p&gt;We will install the bundle Nginx+Passenger shipped by Phusion repositories.&lt;/p&gt;

&lt;p&gt;Add the key to your keyring :&lt;/p&gt;

&lt;pre&gt;
# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7&lt;/pre&gt;

&lt;p&gt;The Phusion repository uses HTTPS, add HTTPS transport to APT :&lt;/p&gt;

&lt;pre&gt;
# apt-get install apt-transport-https ca-certificates&lt;/pre&gt;

&lt;p&gt;Finally, install Nginx and Passenger :&lt;/p&gt;

&lt;pre&gt;
# apt-get update
# apt-get install nginx-extras passenger&lt;/pre&gt;

&lt;h3&gt;Configure Nginx and Puppetmaster application&lt;/h3&gt;

&lt;p&gt;Edit &lt;strong&gt;/etc/nginx/nginx.conf&lt;/strong&gt; and uncomment the reference to passenger config :&lt;/p&gt;

&lt;pre&gt;
    ##
    # Phusion Passenger config
    ##
    # Uncomment it if you installed passenger or passenger-enterprise
    ##

    include /etc/nginx/passenger.conf;&lt;/pre&gt;

&lt;p&gt;Create the file &lt;strong&gt;/etc/nginx/nginx/sites-available/puppet.conf&lt;/strong&gt; with the following content :&lt;/p&gt;

&lt;pre&gt;
server {
    listen                     8140 ssl;
    server_name                puppet puppetmaster puppetmaster.example.com;

    passenger_enabled          on;
    passenger_app_env          production;

    passenger_set_header       X-Client-Verify  $ssl_client_verify;
    passenger_set_header       X-Client-DN $ssl_client_s_dn;
    passenger_set_header       X-SSL-Subject    $ssl_client_s_dn;
    passenger_set_header       X-SSL-Issuer     $ssl_client_i_dn;

    access_log                 /var/log/nginx/puppet_access.log;
    error_log                  /var/log/nginx/puppet_error.log;

    root                       /etc/puppet/rack/public;

    ssl_certificate            /var/lib/puppet/ssl/certs/puppetmaster.example.com.pem;
    ssl_certificate_key        /var/lib/puppet/ssl/private_keys/puppetmaster.example.com.pem;
    ssl_crl                    /var/lib/puppet/ssl/ca/ca_crl.pem;
    ssl_client_certificate     /var/lib/puppet/ssl/certs/ca.pem;
    ssl_ciphers                'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
    ssl_prefer_server_ciphers  on;
    ssl_verify_client          optional;
    ssl_verify_depth           1;
    ssl_session_cache          shared:SSL:128m;
    ssl_session_timeout        5m;
}&lt;/pre&gt;

&lt;p&gt;Remove the default virtual host from Nginx as we don't need it :&lt;/p&gt;

&lt;pre&gt;
# rm /etc/nginx/sites-enabled/default&lt;/pre&gt;

&lt;p&gt;And enable your newly created server :&lt;/p&gt;

&lt;pre&gt;
# ln -s /etc/nginx/sites-available/puppet.conf /etc/nginx/sites-enabled/puppet.conf&lt;/pre&gt;

&lt;p&gt;Before restarting Nginx, we will configure the Ruby application for Puppetmaster.&lt;/p&gt;

&lt;p&gt;Create the directory &lt;strong&gt;/etc/puppet/rack&lt;/strong&gt; and its subdirectories &lt;strong&gt;/etc/puppet/rack/public&lt;/strong&gt; and &lt;strong&gt;/etc/puppet/rack/tmp&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;
# mkdir -p /etc/puppet/rack/public /etc/puppet/rack/tmp&lt;/pre&gt;

&lt;p&gt;Create the file &lt;strong&gt;/etc/puppet/rack/config.ru&lt;/strong&gt; with the following content :&lt;/p&gt;

&lt;pre&gt;
# a config.ru, for use with every rack-compatible webserver.
# SSL needs to be handled outside this, though.

# if puppet is not in your RUBYLIB:
# $LOAD_PATH.unshift('/opt/puppet/lib')

$0 = &quot;master&quot;

# Set the PATH in environment variable
ENV['PATH'] = &quot;/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin&quot;

# if you want debugging:
# ARGV &amp;lt;&amp;lt; &quot;--debug&quot;

ARGV &amp;lt;&amp;lt; &quot;--rack&quot;

# Rack applications typically don't start as root.  Set --confdir, --vardir,
# --logdir, --rundir to prevent reading configuration from
# ~/ based pathing.
ARGV &amp;lt;&amp;lt; &quot;--confdir&quot; &amp;lt;&amp;lt; &quot;/etc/puppet&quot;
ARGV &amp;lt;&amp;lt; &quot;--vardir&quot;  &amp;lt;&amp;lt; &quot;/var/lib/puppet&quot;
ARGV &amp;lt;&amp;lt; &quot;--logdir&quot;  &amp;lt;&amp;lt; &quot;/var/log/puppet&quot;
ARGV &amp;lt;&amp;lt; &quot;--rundir&quot;  &amp;lt;&amp;lt; &quot;/var/run/puppet&quot;
#ARGV &amp;lt;&amp;lt; &quot;--codedir&quot;  &amp;lt;&amp;lt; &quot;/etc/puppet/code&quot;

# always_cache_features is a performance improvement and safe for a master to
# apply. This is intended to allow agents to recognize new features that may be
# delivered during catalog compilation.
ARGV &amp;lt;&amp;lt; &quot;--always_cache_features&quot;

# NOTE: it's unfortunate that we have to use the &quot;CommandLine&quot; class
#  here to launch the app, but it contains some initialization logic
#  (such as triggering the parsing of the config file) that is very
#  important.  We should do something less nasty here when we've
#  gotten our API and settings initialization logic cleaned up.
#
# Also note that the &quot;$0 = master&quot; line up near the top here is
#  the magic that allows the CommandLine class to know that it's
#  supposed to be running master.
#
# --cprice 2012-05-22

require 'puppet/util/command_line'
# we're usually running inside a Rack::Builder.new {} block,
# therefore we need to call run *here*.
run Puppet::Util::CommandLine.new.execute&lt;/pre&gt;

&lt;p&gt;Chown the file for Puppet user :&lt;/p&gt;

&lt;pre&gt;
# chown puppet:puppet /etc/puppet/rack/config.ru&lt;/pre&gt;

&lt;p&gt;And finally, restart Nginx :&lt;/p&gt;

&lt;pre&gt;
# service nginx restart&lt;/pre&gt;

&lt;p&gt;Then, test you configuration by running the agent :&lt;/p&gt;

&lt;pre&gt;
# puppet agent --test&lt;/pre&gt;

&lt;h3&gt;Troubleshooting and errors&lt;/h3&gt;

&lt;h4&gt;Error 500&lt;/h4&gt;

&lt;pre&gt;
Warning: Error 500 on SERVER: Internal Server Error&lt;/pre&gt;

&lt;p&gt;Read the logs at &lt;strong&gt;/var/log/nginx/error.log&lt;/strong&gt; and &lt;strong&gt;/etc/nginx/puppet_error.log&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;Error 403&lt;/h4&gt;

&lt;pre&gt;
Warning: Error 403 on SERVER: Forbidden request: localhost(127.0.0.1) access to /node/puppetmaster.example.com [find] at :119&lt;/pre&gt;

&lt;p&gt;Check that your hostname resolves, and that your host file is clean. In particular, you should have the host name of your server on the same line than localhost :&lt;/p&gt;

&lt;pre&gt;
127.0.0.1    localhost puppetmaster puppetmaster.example.com&lt;/pre&gt;

&lt;p&gt;Also check that your Puppet configuration is correct, in particular check that the two lines &quot;required for Passenger&quot; are commented.&lt;/p&gt;

&lt;h3&gt;Sources&lt;/h3&gt;

&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;https://docs.puppet.com/puppet/4.5/reference/passenger.html#install-rackpassenger&quot;&gt;[Puppet Doc] Configuring a Puppet Master Server with Passenger and Apache&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://www.linode.com/docs/websites/ror/ruby-on-rails-nginx-debian-8&quot;&gt;[Linode] Ruby on Rails with Nginx on Debian 8&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://www.phusionpassenger.com/library/install/nginx/install/oss/jessie/&quot;&gt;[Phusion Passenger] Installing Passenger + Nginx&lt;/a&gt;
	&lt;ul&gt;
		&lt;li&gt;&lt;a href=&quot;https://www.phusionpassenger.com/library/config/nginx/reference/&quot;&gt;[Phusion Passenger] Configuration reference&lt;/a&gt;&lt;/li&gt;
	&lt;/ul&gt;
	&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://ask.puppet.com/question/13176/puppet-master-could-not-retrieve-fact-fqdnipaddress/?answer=13351#post-id-13351&quot;&gt;[Ask Puppet] Puppet Master - Could not retrieve fact fqdn/ipaddress&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a class=&quot;question-hyperlink&quot; href=&quot;http://serverfault.com/questions/456680/puppet-master-rest-api-returns-403-when-running-under-passenger-works-when-maste&quot;&gt;Puppet master REST API returns 403 when running under passenger&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;</description>
        
              </item>
          <item>
        <title>Blattaria Momentum</title>
        <link>https://uname.pingveno.net/blog/index.php/post/2016/06/20/Blattaria-Momentum</link>
        <guid isPermaLink="false">urn:md5:118d1613433df89c7f0081de75c2d2a5</guid>
        <pubDate>Mon, 20 Jun 2016 09:53:00 +0200</pubDate>
        <dc:creator>Mathieu</dc:creator>
                  <category>My Life</category>
                        <description>&lt;p&gt;Ou « ce que je n'ai pas dit à l'oraison funèbre, parce que c'est pas mon truc de parler devant des gens tristes pour les faire pleurer ».&lt;/p&gt;          &lt;p&gt; &lt;/p&gt;

&lt;p&gt;Lundi dernier, nous avons perdu quelqu'un. À titre strictement personnel, je ne le connaissais pas si bien que ça.&lt;/p&gt;

&lt;p&gt;C'est à l'occasion de mes apparitions aux rendez-vous et événements du BDE que j'ai eu l'occasion de le rencontrer. Et même si je n'ai passé en cumulé qu'une centaine d'heures avec lui, sa disparition m'a troublé à un point auquel je ne m'attendais pas.&lt;/p&gt;

&lt;p&gt;J'ai été d'autant plus touché qu'il était né la même année que moi, presque le même mois. Je me sentais très proche et je me reconnaissais beaucoup dans sa manière de réagir et d'interpréter les événements et les problèmes. L'impression de perdre quelqu'un que l'on regrette d'avoir connu ajoute de la déception et du regret au chagrin.&lt;/p&gt;

&lt;p&gt;Je ne le connaissais pas si bien que ça, mais c'était quelqu'un d'immédiatement attachant, souriant, et qui allait spontanément vers les autres. C'était quelqu'un de sérieux, d'engagé, de rigoureux et fidèle dans ses engagements. Lundi dernier, nous avons perdu un ami fidèle, un membre émérite de l'association, et une très grande personne.&lt;/p&gt;

&lt;p&gt;Certains le connaissaient pour ses colères ou ses prises de position tranchées, sa passion un peu trop féroce pour convaincre du bien fondé de son point de vue, mais une fois l'orage passé il avait l'humilité, l'intelligence, et le courage de reconnaître lorsqu'il s'était trompé. Des qualités trop rares de nos jours.&lt;/p&gt;

&lt;p&gt;Profondément rationnel, logique, mais aussi humain et attentif à la souffrance des autres, son geste est d'autant plus incompréhensible. Au point où l'accident stupide semble être la seule explication rationnelle que l'on peut admettre.&lt;/p&gt;

&lt;p&gt;De son engagement associatif, humain, et scientifique, il restera dans nos mémoires comme un fils, un frère,&amp;nbsp;un modèle, ou simplement un ami.&lt;/p&gt;

&lt;p&gt;Adieu Jean. Et merci.&lt;/p&gt;</description>
        
              </item>
      </channel>
</rss>
