{"version":"1.0","encoding":"UTF-8","feed":{"xmlns":"http://www.w3.org/2005/Atom","xmlns$openSearch":"http://a9.com/-/spec/opensearch/1.1/","xmlns$blogger":"http://schemas.google.com/blogger/2008","xmlns$georss":"http://www.georss.org/georss","xmlns$gd":"http://schemas.google.com/g/2005","xmlns$thr":"http://purl.org/syndication/thread/1.0","gd$etag":"W/\"DkENSXo_cCp7ImA9Xh5VEUw.\"","id":{"$t":"tag:blogger.com,1999:blog-6816195014656817423"},"updated":{"$t":"2026-08-07T07:58:18.448-07:00"},"category":[{"term":"Linux"},{"term":"Free Web"},{"term":"Encryption"},{"term":"Development"},{"term":"Security"},{"term":"Web Hosting"},{"term":"Apache"},{"term":"Nginx"},{"term":"PostgreSQL"},{"term":"Web Development"},{"term":"Email"},{"term":"Web Security"},{"term":"Backup"},{"term":"Online Identity \u0026 Privacy"},{"term":"Thunderbird"},{"term":"Windows"},{"term":"Home Assistant"},{"term":"NodeJS"},{"term":"macOS"},{"term":"Android"},{"term":"Exchange"},{"term":"Go"},{"term":"Office 365"},{"term":"Python"},{"term":"Server monitoring"},{"term":"Ubuntu"},{"term":"Webhosting monitoring"},{"term":"socket.io"}],"title":{"$t":"b247.eu.org | Just another information technology blog"},"subtitle":{"type":"html","$t":"A blog about SECURITY, DEVELOPMENT, FREE WEB, LINUX and other daily use technologies."},"link":[{"rel":"http://schemas.google.com/g/2005#feed","type":"application/atom+xml","href":"https:\/\/www.b247.eu.org\/feeds\/posts\/default"},{"rel":"self","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default?alt=json\u0026max-results=10\u0026v=2"},{"rel":"alternate","type":"text/html","href":"https:\/\/www.b247.eu.org\/"},{"rel":"hub","href":"http://pubsubhubbub.appspot.com/"},{"rel":"next","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default?alt=json\u0026start-index=11\u0026max-results=10\u0026v=2"}],"author":[{"name":{"$t":"b247.eu.org"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/05580857583369963260"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"32","height":"32","src":"\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhCOFmryd_Wq7VuVeGmjJ4NXGLdVWFzH6sMG0LOgTugMQaZvlG44DsimJ7u7mhOuWfM5kC1eF9YAieX1YdqiWauCF_Imxv6HXrqaQIiyG5-52rceHYo9fYFhENwp9dKy2g\/s113\/logo600px.png"}}],"generator":{"version":"7.00","uri":"http://www.blogger.com","$t":"Blogger"},"openSearch$totalResults":{"$t":"38"},"openSearch$startIndex":{"$t":"1"},"openSearch$itemsPerPage":{"$t":"10"},"entry":[{"gd$etag":"W/\"C0cHQno8cCp7ImA9Xh5WE0k.\"","id":{"$t":"tag:blogger.com,1999:blog-6816195014656817423.post-7647223063401415189"},"published":{"$t":"2026-07-29T09:03:53.477-07:00"},"updated":{"$t":"2026-07-29T09:03:53.478-07:00"},"app$edited":{"xmlns$app":"http://www.w3.org/2007/app","$t":"2026-07-29T09:03:53.478-07:00"},"category":[{"scheme":"http://www.blogger.com/atom/ns#","term":"Home Assistant"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Linux"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Security"}],"title":{"$t":" Secure Remote Access to Home Assistant with Cloudflare Zero Trust Tunnels"},"content":{"type":"html","$t":"\u003Cnoscript\u003E\n\u003Cimg alt='Cloudflare Zero Trust Tunnels for Home Assistant' width='856' height='397' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEgb3QrrNBPoZzyeGIiTt3gPbSzNC_vevIz3HRekYjeTwcaSzPnFPtRRiyQcQVVWgs40WY5IZGoJZAie2853eZ62ysz7sRJ2ZkDJKhycbEhPdXokTlOVJcNcQkLCdkpvz9otobCGLAEFNlBaRvPar5fXeVDiOl10GRBNcR6Roh2WeHIZxpRRny0LitQbxmIe\/s1600\/home-assistant-cloudflared.webp' \/\u003E\n\u003C\/noscript\u003E\n\n\u003Cp\u003EIt can be tempting to keep Home Assistant OS (HAOS) completely isolated on your local network, especially if you want to avoid fiddling with router port forwarding or managing complex VPN solutions. However, keeping HAOS locked down like this limits some of Home Assistant's best features—such as controlling your smart home while away, receiving real-time push notifications, or watching low-latency live streams from Frigate cameras via WebRTC.\u003C\/p\u003E\n\n\u003Cp\u003EIf setting up and maintaining port forwarding or a dedicated VPN feels like overkill (and it is), there is a modern, top-notch alternative: Cloudflare Zero Trust Tunnels.\u003C\/p\u003E\n\n\u003Cp\u003ETo set this up, all you need is a free Cloudflare account and a custom domain (which you can register directly through Cloudflare for as little as $5\/year for a .uk TLD).\u003C\/p\u003E\n\n\u003Ch2\u003ESetting Up a Cloudflare Zero Trust Tunnel\u003C\/h2\u003E\n\u003Cli\u003EFrom the Cloudflare dashboard, use the quick search bar in the left navigation panel (located right under your username) to search for and access \u003Cstrong\u003EZero Trust\u003C\/strong\u003E.\u003C\/li\u003E\n\u003Cli\u003EIn the \u003Cstrong\u003EZero Trust dashboard\u003C\/strong\u003E, navigate to \u003Cstrong\u003ENetworks\u003C\/strong\u003E \u0026rarr; \u003Cstrong\u003ETunnels \u0026 Mesh\u003C\/strong\u003E (or \u003Cstrong\u003ETunnels\u003C\/strong\u003E).\u003C\/li\u003E\n\u003Cli\u003EClick the \u003Cstrong\u003ECreate a tunnel\u003C\/strong\u003E button and select \u003Cstrong\u003ECloudflared\u003C\/strong\u003E as your connector type.\u003C\/li\u003E\n\u003Cli\u003EEnter a descriptive name for your tunnel (such as home.tun or myhome.ha.tun), then click Save tunnel.\u003C\/li\u003E\n\n\u003Cp\u003EAfter saving, the Configure page will appear with generic OS installation instructions. What you need here is the generated access token located near the bottom under the option to Run the tunnel manually:\u003Cbr\/\u003E\n\u003Ccode\u003Ecloudflared tunnel run --token eyJh...\u003C\/code\u003E\u003Cbr\/\u003E\nCopy this command (to extract the token string directly following --token) and keep it ready for the next step.\n\u003Cli\u003Ethen click \u003Cstrong\u003ENext\u003C\/strong\u003E and \u003Cstrong\u003EComplete setup\u003C\/strong\u003E\u003C\/li\u003E\n\u003C\/p\u003E\n\n\n\u003Ch2\u003EConnecting Home Assistant to Your Cloudflare Tunnel\u003C\/h2\u003E\n\u003Cli\u003EIn Home Assistant, navigate to \u003Cstrong\u003ESettings\u003C\/strong\u003E \u0026rarr; \u003Cstrong\u003EApps\u003C\/strong\u003E (formerly Add-ons), search for \u003Cstrong\u003ECloudflared\u003C\/strong\u003E, and click \u003Cstrong\u003EInstall\u003C\/strong\u003E.\n\u003Cstrong\u003EImportant:\u003C\/strong\u003E Do not start the app yet! Before running it, you must configure the access token copied from the Cloudflare dashboard.\u003C\/li\u003E\n\u003Cli\u003EGo to the \u003Cstrong\u003EConfiguration\u003C\/strong\u003E tab. Set \u003Cstrong\u003EExternal Home Assistant Hostname\u003C\/strong\u003E to your desired domain (e.g., \u003Ccode\u003Ehomeassistant.yourdomain.com\u003C\/code\u003E—we will use this later in the Cloudflare Tunnels dashboard), paste your token into \u003Cstrong\u003ECloudflare Tunnel Token\u003C\/strong\u003E, and click \u003Cstrong\u003ESave\u003C\/strong\u003E.\u003C\/li\u003E\n\n\n\u003Ctable align='center' cellpadding='0' cellspacing='7' class='tr-caption-container clear' width='725'\u003E\n    \u003Ctbody\u003E\n        \u003Ctr\u003E\n            \u003Ctd class='center'\u003E\n                \u003Camp-img alt='Home Assistant Cloudflared settings' layout='responsive'\n                    lightbox=''\n                    src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhj52NirBjeLP8POG8idQFu90WankvoTvyu2cnFt0tFHDO2iqLBUbQrlcZnpUynQ5KCvEVrEIWX0CmsQslg0AgISKCTL2-SBrdK_lu8O72eJXNZxcQWiYKRW2h9r4odpHInFWGaWCsTldqWHpEysGuJZ0JuNEozXzYonhwn7PHSR0m-71F-_oxII6Ph-Xpk\/s1600\/haos-cloudflared-optim.jpg'\n                    tabindex='0' width='725' height='450'\u003E\u003C\/amp-img\u003E\n            \u003C\/td\u003E\n        \u003C\/tr\u003E\n        \u003Ctr\u003E\n            \u003Ctd class='tr-caption'\u003EHome Assistant Cloudflared settings.\u003C\/td\u003E\n        \u003C\/tr\u003E\n    \u003C\/tbody\u003E\n\u003C\/table\u003E\n\n\u003Cli\u003EReturn to the \u003Cstrong\u003EInfo\u003C\/strong\u003E tab. Toggle on \u003Cstrong\u003EStart on boot\u003C\/strong\u003E, \u003Cstrong\u003EWatchdog\u003C\/strong\u003E, and \u003Cstrong\u003EAuto update\u003C\/strong\u003E, then click \u003Cstrong\u003EStart\u003C\/strong\u003E to manually start the Cloudflared app.\u003C\/li\u003E\n\n\u003Cp\u003EBecause Home Assistant restricts reverse proxy connections by default, you must configure \u003Ccode\u003Econfiguration.yaml\u003C\/code\u003E to trust traffic coming from the Cloudflared app. Open \u003Ccode\u003Econfiguration.yaml\u003C\/code\u003E (\/homeassistant\/configuration.yaml) using your preferred method (Terminal, File Editor, or VS Code) and append the snippet below to the end of the file:\u003C\/p\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003E\n# cloudflared\nhttp:\n  use_x_forwarded_for: true\n  trusted_proxies:\n    - 172.30.32.0\/23\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\u003Cp\u003ESave the file and restart Home Assistant (\u003Cstrong\u003ESettings\u003C\/strong\u003E \u0026rarr; top-right three-dot menu \u0026rarr; \u003Cstrong\u003ERestart Home Assistant\u003C\/strong\u003E). The Cloudflared app will autostart.\u003C\/p\u003E\n\n\u003Ch3\u003EPublish an Application Route for Home Assistant\u003C\/h3\u003E\n\u003Cp\u003EReturn to the \u003Cstrong\u003ECloudflare portal\u003C\/strong\u003E (\u003Cstrong\u003ENetworks\u003C\/strong\u003E \u0026rarr; \u003Cstrong\u003ETunnels\u003C\/strong\u003E). A status of \u003Cstrong\u003EHealthy\u003C\/strong\u003E should now appear next to your tunnel. Click your tunnel name, navigate to \u003Cstrong\u003EPublished application routes\u003C\/strong\u003E, and click \u003Cstrong\u003EAdd a published application route\u003C\/strong\u003E.\u003C\/p\u003E\n\u003Cli\u003ESelect a \u003Cstrong\u003ESubdomain\u003C\/strong\u003E and \u003Cstrong\u003EDomain\u003C\/strong\u003E so that the resulting \u003Cstrong\u003EFull hostname\u003C\/strong\u003E matches the exact FQDN you configured earlier in the Cloudflared app (e.g., \u003Ccode\u003Ehomeassistant.yourdomain.com\u003C\/code\u003E) and click \u003Cstrong\u003E\u003ESave\u003C\/strong\u003E.\u003C\/li\u003E\n\n\u003Ctable align='center' cellpadding='0' cellspacing='7' class='tr-caption-container clear' width='725'\u003E\n    \u003Ctbody\u003E\n        \u003Ctr\u003E\n            \u003Ctd class='center'\u003E\n                \u003Camp-img alt='Cloudflare Tunnel Application Route for Home Assistant' layout='responsive'\n                    lightbox=''\n                    src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEjreLRAxV3qFsrOoi3SqCx1cpqKuN59NPQm7raQ4w0QbDbMnjzBj3s1jaJyVb2ZApnvMicqkLDITK4Gflutt53Nv2YXT-noHwaoMKDLpFDzaqvm3NIn1x59Ks8xQiXcJl6RY0jkFACFVam26JcCPmSVqG-l5TGQ6IZccC22D-41D05Yzt-y6QmjslJgOcLD\/s1600\/haos-clouflare-route.webp'\n                    tabindex='0' width='725' height='450'\u003E\u003C\/amp-img\u003E\n            \u003C\/td\u003E\n        \u003C\/tr\u003E\n        \u003Ctr\u003E\n            \u003Ctd class='tr-caption'\u003ECloudflare Tunnel Application Route for Home Assistant.\u003C\/td\u003E\n        \u003C\/tr\u003E\n    \u003C\/tbody\u003E\n\u003C\/table\u003E\n\n\u003Cp\u003ESet the Home Assistant Network settings (\u003Cstrong\u003ESettings\u003C\/strong\u003E \u0026rarr; \u003Cstrong\u003ESystem\u003C\/strong\u003E \u0026rarr; \u003Cstrong\u003ENetwork\u003C\/strong\u003E) \u003Cstrong\u003EHome Assistant URL\u003C\/strong\u003E to match \u003Ccode\u003Ehttps:\/\/homeassistant.yourdomain.com\u003C\/code\u003E, while keeping your local network URL as-is.\u003C\/p\u003E\n\n\u003Cp\u003EThat's it! You can now securely access your Home Assistant OS instance via web browser or via the Companion app using your custom FQDN address.\u003C\/p\u003E\n\n\u003Cp\u003E\u003Cstrong\u003EFinal Words:\u003C\/strong\u003E Even if your Home Assistant FQDN isn't publicly indexed, anyone who discovers your URL can still attempt to log in. As a rule of thumb, always enable Multi-Factor Authentication (MFA\/2FA) on every user account. That way, even in the event of an unfortunate leak of your URL, username, and password, no one can gain access without that second factor.\u003C\/p\u003E\n"},"link":[{"rel":"edit","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/7647223063401415189?v=2"},{"rel":"self","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/7647223063401415189?v=2"},{"rel":"alternate","type":"text/html","href":"https:\/\/www.b247.eu.org\/2026\/07\/home-assistant-remote-cloudflared.html","title":" Secure Remote Access to Home Assistant with Cloudflare Zero Trust Tunnels"}],"author":[{"name":{"$t":"b247.eu.org"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/05580857583369963260"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"32","height":"32","src":"\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhCOFmryd_Wq7VuVeGmjJ4NXGLdVWFzH6sMG0LOgTugMQaZvlG44DsimJ7u7mhOuWfM5kC1eF9YAieX1YdqiWauCF_Imxv6HXrqaQIiyG5-52rceHYo9fYFhENwp9dKy2g\/s113\/logo600px.png"}}],"media$thumbnail":{"xmlns$media":"http://search.yahoo.com/mrss/","url":"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEgb3QrrNBPoZzyeGIiTt3gPbSzNC_vevIz3HRekYjeTwcaSzPnFPtRRiyQcQVVWgs40WY5IZGoJZAie2853eZ62ysz7sRJ2ZkDJKhycbEhPdXokTlOVJcNcQkLCdkpvz9otobCGLAEFNlBaRvPar5fXeVDiOl10GRBNcR6Roh2WeHIZxpRRny0LitQbxmIe\/s72-c\/home-assistant-cloudflared.webp","height":"72","width":"72"}},{"gd$etag":"W/\"AkcGQXw9cCp7ImA9Xh5WEUs.\"","id":{"$t":"tag:blogger.com,1999:blog-6816195014656817423.post-3050531618472328722"},"published":{"$t":"2026-07-27T08:57:41.872-07:00"},"updated":{"$t":"2026-07-27T09:00:20.268-07:00"},"app$edited":{"xmlns$app":"http://www.w3.org/2007/app","$t":"2026-07-27T09:00:20.268-07:00"},"category":[{"scheme":"http://www.blogger.com/atom/ns#","term":"Free Web"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Go"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Home Assistant"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Python"}],"title":{"$t":"iHidro: Hidroelectrica Integration for Home Assistant"},"content":{"type":"html","$t":"\u003C!-- iHidro: Hidroelectrica Integration for Home Assistant --\u003E\n\n\u003Cnoscript\u003E\n\u003Cimg alt='iHidroHA, iHidroGo: Hidroelectrica API consumer and Integration for Home Assistant' width='856' height='397' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEjVsc0W5GWaMICyzwWripTMsUnlJpPBBWdod22g9jFYIIkLZkgkTmSI61wGVLdZdBuEdGt3x-Ro4IWmyrKHm7sRik0-zMWBqWClVBr0MEohyEmlW8NPztTybMOQNPRtdE40idfmRJygGcATFpo2IRVmyyoiqUs0AWzxqL4V7AwnorQhyYFAwGf6-qqRpxoP\/s1600\/iHidroHA-optim.jpg' \/\u003E\n\u003C\/noscript\u003E\n\n\u003Cp\u003EOne might ask about the benefits of a custom Home Assistant integration when official iOS, Android, and web applications already exist for Hidroelectrica accounts. For me, it comes down to convenience and centralized tracking. It gives me an automated way to submit self-read meter index updates—whether I'm away on vacation or simply don't want to check the meter manually. More importantly, it keeps both monthly and total energy consumption metrics clean and visible directly on my custom Home Assistant dashboard.\u003C\/p\u003E\n\n\u003Cp\u003ETo bridge this gap without relying on manual app entry, I built two complementary projects: \u003Ca href=\"https:\/\/github.com\/b247\/iHidroGo\" target=\"_blank\" rel=\"nofollow noreferrer noopener\"\u003EiHidroGo\u003C\/a\u003E (a standalone Go client\/library that interfaces directly with Hidroelectrica's SEW API server) and \u003Ca href=\"https:\/\/github.com\/b247\/iHidroHA\" target=\"_blank\" rel=\"nofollow noreferrer noopener\"\u003EiHidroHA\u003C\/a\u003E (the custom HACS integration that brings those API capabilities natively into Home Assistant as actionable services).\u003C\/p\u003E\n\n\u003Ch2\u003EPairing Hidroelectrica Index Submissions with Home Assistant Power Meters\u003C\/h2\u003E\n\n\u003Cp\u003EOf course, to get the full benefit of this setup, adding a dedicated smart power meter like a Shelly PM to your Home Assistant ecosystem is ideal. When you're away on vacation, having an automated \"digital\" index ready to submit to Hidroelectrica makes all the difference—and a device like the Shelly PM tracks that continuous usage for you.\u003C\/p\u003E\n\n\u003Cp\u003EWhile a physical smart meter isn't strictly required to use the integration, installing a Shelly monitor in my main breaker panel (TGBT) is what actually sparked this project, allowing me to fully automate and benefit from the entire solution.\u003C\/p\u003E\n\n\n\u003Ch2\u003EIntegrating iHidroHA with Home Assistant\u003C\/h2\u003E\n\n\u003Cp\u003EInstead of continuous background polling, \u003Cstrong\u003EiHidroHA\u003C\/strong\u003E introduces native Home Assistant actions (services) that give you direct, programmatic control over index submissions and history retrieval:\u003C\/p\u003E\n\n\u003Cul\u003E\n  \u003Cli\u003E\u003Cstrong\u003E\u003Ccode\u003Eihidro.submit_index\u003C\/code\u003E:\u003C\/strong\u003E Submits your current index reading directly to the iHidro API.\u003C\/li\u003E\n  \u003Cli\u003E\u003Cstrong\u003E\u003Ccode\u003Eihidro.get_index_history\u003C\/code\u003E:\u003C\/strong\u003E Fetches historical meter readings associated with your contract.\u003C\/li\u003E\n\u003C\/ul\u003E\n\n\u003Ch3\u003EInstallation via HACS\u003C\/h3\u003E\n\n\u003Col\u003E\n  \u003Cli\u003EOpen \u003Cstrong\u003EHACS\u003C\/strong\u003E in Home Assistant \u0026rarr; Click the \u003Cstrong\u003Ethree dots menu\u003C\/strong\u003E (top right) \u0026rarr; Select \u003Cstrong\u003ECustom repositories\u003C\/strong\u003E.\u003C\/li\u003E\n  \u003Cli\u003EAdd \u003Ccode\u003Ehttps:\/\/github.com\/b247\/iHidroHA\u003C\/code\u003E with the category (Type) set to \u003Cstrong\u003EIntegration\u003C\/strong\u003E.\u003C\/li\u003E\n  \u003Cli\u003EClick \u003Cstrong\u003EDownload\u003C\/strong\u003E. HACS will trigger a system notification letting you know that Home Assistant needs a restart to load the integration.\u003C\/li\u003E\n  \u003Cli\u003ENavigate to \u003Cstrong\u003ESettings\u003C\/strong\u003E \u0026rarr; \u003Cstrong\u003EDevices \u0026amp; Services\u003C\/strong\u003E \u0026rarr; \u003Cstrong\u003EAdd Integration\u003C\/strong\u003E \u0026rarr; Search for \u003Cstrong\u003EiHidro\u003C\/strong\u003E.\u003C\/li\u003E\n  \u003Cli\u003EEnter your iHidro\/Hidroelectrica account credentials and your \u003Cstrong\u003EUAN (Contract Number)\u003C\/strong\u003E.\u003C\/li\u003E\n\u003C\/ol\u003E\n\n\u003Ch3\u003EAutomation Example: Automated Submission \u0026amp; Meter Reset\u003C\/h3\u003E\n\n\u003Cp\u003EHere is how to automate the submission using a numeric helper (\u003Ccode\u003Einput_number\u003C\/code\u003E). When the helper state updates, Home Assistant submits the index value via \u003Ccode\u003Eihidro.submit_index\u003C\/code\u003E. If the API call succeeds, it resets the local utility meter and fires a notification:\u003C\/p\u003E\n\n\u003Cdiv class=\"code info\"\u003E\u003Ci class=\"material-icons large3em\"\u003Einfo\u003C\/i\u003E\n  \u003Cstrong\u003EPro Tip:\u003C\/strong\u003E For a quick test drive, you can temporarily comment out the \u003Ccode\u003Eutility_meter.reset\u003C\/code\u003E action and swap the trigger \u003Ccode\u003Eentity_id\u003C\/code\u003E with your own \u003Cstrong\u003EInput Number helper\u003C\/strong\u003E (created via \u003Cstrong\u003ESettings \u0026rarr; Devices \u0026amp; Services \u0026rarr; Helpers\u003C\/strong\u003E).\n\u003C\/div\u003E\n\u003Cp\u003E\u003C\/p\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003E\nalias: Submit iHidro index and Reset Shelly Monthly Meter\ndescription: \"\"\ntriggers:\n  - entity_id: input_number.shellypluspmminitgbt_em_offset\n    trigger: state\nconditions:\n  - condition: template\n    value_template: \"{{ trigger.from_state.state not in ['unavailable', 'unknown'] }}\"\n  - condition: template\n    value_template: \"{{ trigger.to_state.state not in ['unavailable', 'unknown'] }}\"\nactions:\n  - action: ihidro.submit_index\n    data:\n      value: \"{{ trigger.to_state.state | int }}\"\n    response_variable: api_result\n\n  - if:\n      - condition: template\n        value_template: \"{{ api_result.success }}\"\n    then:\n      - action: utility_meter.reset\n        target:\n          entity_id: sensor.hallway_shellypluspmminitgbt_meter\n      - action: persistent_notification.create\n        data:\n          title: \"iHidro Submission Success\"\n          message: \"{{ api_result.output }}\"\n    else:\n      - action: persistent_notification.create\n        data:\n          title: \"iHidro Submission Failed\"\n          message: \"{{ api_result.output }}\"\nmode: single\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\n\u003Ch2\u003ESetting Up Your Helpers and Custom Dashboard Card\u003C\/h2\u003E\n\u003Cp\u003EBefore adding the UI card to your Lovelace dashboard, you need to create three core helpers under \u003Cstrong\u003ESettings \u0026rarr; Devices \u0026amp; Services \u0026rarr; Helpers\u003C\/strong\u003E:\u003C\/p\u003E\n\n\u003Col\u003E\n  \u003Cli\u003E\u003Cstrong\u003EInput Number (\u003Ccode\u003Einput_number.shellypluspmminitgbt_em_offset\u003C\/code\u003E):\u003C\/strong\u003E Holds the submission trigger value. Updating this number fires the \u003Ccode\u003Eihidro.submit_index\u003C\/code\u003E action.\u003C\/li\u003E\n  \u003Cli\u003E\u003Cstrong\u003ETemplate Sensor (\u003Ccode\u003Esensor.hallway_shellypluspmminitgbt_index\u003C\/code\u003E):\u003C\/strong\u003E Computes your current physical index reading by combining your baseline offset with live Shelly meter accumulation. Use \u003Ccode\u003E {{ (states('input_number.shellypluspmminitgbt_em_offset') | float(0) + states('sensor.hallway_shellypluspmminitgbt_meter') | float(0)) | round(2) }}\u003C\/code\u003E but with your own IDs.\u003C\/li\u003E\n  \u003Cli\u003E\u003Cstrong\u003EUtility Meter (\u003Ccode\u003Esensor.hallway_shellypluspmminitgbt_meter\u003C\/code\u003E):\u003C\/strong\u003E Tracks your current cycle's energy consumption (kWh). It automatically resets back to zero upon a successful iHidro submission.\u003C\/li\u003E\n\u003C\/ol\u003E\n\n\u003Cdiv class=\"code info\"\u003E\u003Ci class=\"material-icons large3em\"\u003Einfo\u003C\/i\u003E\n  \u003Cstrong\u003ENote on Entity Names:\u003C\/strong\u003E Be sure to adjust the entity IDs in the YAML snippet below (e.g., changing \u003Ccode\u003Ehallway_shellypluspmminitgbt\u003C\/code\u003E) to match the actual names you assigned to your helpers and Shelly entities in Home Assistant.\n\u003C\/div\u003E\n\u003Cp\u003E\u003C\/p\u003E\n\n\u003Ch3\u003ECustom Dashboard Card UI\u003C\/h3\u003E\n\u003Cp\u003ETo pull everything together into a clean interface, pair your power meter metrics and interactive helper triggers using a \u003Ccode\u003Ecustom:stack-in-card\u003C\/code\u003E layout:\u003C\/p\u003E\n\n\u003Ctable align='center' cellpadding='0' cellspacing='7' class='tr-caption-container clear' width='725'\u003E\n    \u003Ctbody\u003E\n        \u003Ctr\u003E\n            \u003Ctd class='center'\u003E\n                \u003Camp-img alt='Home Assistant energy dashboard card including iHidro index submission button' layout='responsive'\n                    lightbox=''\n                    src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEgSZN-Q7Sn0cmVfgd3stLNvCvrsvcq63LScmwCJqnrb06XNs-hCMwRL12CeopNPKudxsU0sEy6w28ODtTe7TfcjDT97bDgoKwQjn6pr3DuYxilTpQNYZLuTMy7NO9dZwqyTjv994xPhen6MZalwuy0DrfCr1gIWdS0x40KcHv804YHfSK_NCDcJNmDq6Id5\/s1600\/home-assistant-energy-card-optim.jpg'\n                    tabindex='0' width='725' height='450'\u003E\u003C\/amp-img\u003E\n            \u003C\/td\u003E\n        \u003C\/tr\u003E\n        \u003Ctr\u003E\n            \u003Ctd class='tr-caption'\u003EHome Assistant energy custom card showing real-time, monthly, and total meters with an interactive submission button.\u003C\/td\u003E\n        \u003C\/tr\u003E\n    \u003C\/tbody\u003E\n\u003C\/table\u003E\n\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003E\ntype: custom:stack-in-card\ntitle: \"\"\nmode: horizontal\ncards:\n  - type: gauge\n    entity: sensor.hallway_shellypluspmminitgbt_power\n    name: \"\"\n    min: 0\n    max: 4500\n    needle: true\n    severity:\n      green: 0\n      yellow: 900\n      red: 2300\n    card_mod:\n      style: |\n        ha-card {\n          border-top: 0;\n          border-bottom: 0;\n        }\n  - type: vertical-stack\n    cards:\n      - type: button\n        entity: sensor.hallway_shellypluspmminitgbt_index\n        show_name: false\n        show_state: true\n        show_icon: false\n        icon: mdi:transmission-tower\n        tap_action:\n          action: more-info\n        name: ShellyPlusPMMiniTGBT EM Index\n        color: var(--secondary-text-color)\n        card_mod:\n          style: |\n            ha-card {\n              border:0;\n              padding-block-end:unset!important\n            }\n            ha-card span[class='state'] {\n                color: #ffffff;\n              }\n      - type: button\n        entity: input_number.shellypluspmminitgbt_em_offset\n        show_name: false\n        show_state: false\n        show_icon: true\n        icon: mdi:transmission-tower-export\n        tap_action:\n          action: more-info\n        name: ShellyPlusPMMiniTGBT EM sync iHidro Oltenitei77 and send Index\n        color: var(--secondary-text-color)\n        card_mod:\n          style: |\n            ha-card {\n              border: 0;\n              padding-block:unset!important\n            }\n             ha-state-icon {\n              color: var(--disabled-text-color) !important;\n            }\n      - type: button\n        entity: sensor.hallway_shellypluspmminitgbt_meter\n        show_name: false\n        show_state: true\n        show_icon: false\n        tap_action:\n          action: more-info\n        name: ShellyPlusPMMiniTGBT Monthly Electricity Consumption Meter\n        card_mod:\n          style: |\n            ha-card {\n              border: 0;\n              padding-block-start:unset!important\n            }\n            ha-card span[class='state'] {\n              {% set m_kWh = states('sensor.hallway_shellypluspmminitgbt_meter') | float %}\n              {% if m_kWh \u003C 90 %}\n                color: #4CAF50;  \/* Green *\/\n              {% elif m_kWh \u003C 120 %}\n                color: #FFB347;  \/* Orange *\/\n              {% else %}\n                color: #F98787;  \/* Red *\/\n              {% endif %}\n            }\n  - type: gauge\n    entity: sensor.hallway_shellypluspmminitgbt_current\n    name: \"\"\n    min: 0\n    max: 16\n    needle: true\n    severity:\n      green: 0\n      yellow: 2\n      red: 10\n    card_mod:\n      style: |\n        ha-card {\n          border-top: 0;\n          border-bottom: 0;\n        }\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\u003Cp\u003E\u003Cem\u003EHappy hacking!\u003C\/em\u003E\u003C\/p\u003E"},"link":[{"rel":"edit","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/3050531618472328722?v=2"},{"rel":"self","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/3050531618472328722?v=2"},{"rel":"alternate","type":"text/html","href":"https:\/\/www.b247.eu.org\/2026\/07\/ihidro-hidroelectrica-home-assistant.html","title":"iHidro: Hidroelectrica Integration for Home Assistant"}],"author":[{"name":{"$t":"b247.eu.org"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/05580857583369963260"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"32","height":"32","src":"\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhCOFmryd_Wq7VuVeGmjJ4NXGLdVWFzH6sMG0LOgTugMQaZvlG44DsimJ7u7mhOuWfM5kC1eF9YAieX1YdqiWauCF_Imxv6HXrqaQIiyG5-52rceHYo9fYFhENwp9dKy2g\/s113\/logo600px.png"}}],"media$thumbnail":{"xmlns$media":"http://search.yahoo.com/mrss/","url":"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEjVsc0W5GWaMICyzwWripTMsUnlJpPBBWdod22g9jFYIIkLZkgkTmSI61wGVLdZdBuEdGt3x-Ro4IWmyrKHm7sRik0-zMWBqWClVBr0MEohyEmlW8NPztTybMOQNPRtdE40idfmRJygGcATFpo2IRVmyyoiqUs0AWzxqL4V7AwnorQhyYFAwGf6-qqRpxoP\/s72-c\/iHidroHA-optim.jpg","height":"72","width":"72"}},{"gd$etag":"W/\"Ak4NQn48fCp7ImA9Xh5WEUs.\"","id":{"$t":"tag:blogger.com,1999:blog-6816195014656817423.post-5457090087843652860"},"published":{"$t":"2022-11-06T07:30:00.001-08:00"},"updated":{"$t":"2026-07-27T09:16:33.074-07:00"},"app$edited":{"xmlns$app":"http://www.w3.org/2007/app","$t":"2026-07-27T09:16:33.074-07:00"},"category":[{"scheme":"http://www.blogger.com/atom/ns#","term":"Development"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Exchange"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Office 365"}],"title":{"$t":"Office 365 and No-Reply\/unmonitored email addresses management and usage"},"content":{"type":"html","$t":"\u003Cnoscript\u003E\n    \u003Cimg alt='Office 365 and No-Reply\/unmonitored email addresses management and usage' width='856' height='397'\n        src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEjzw7bRJzvlIFt4scu0hqfeqVkF_29uZxGO_I3Ald6tupbIvPKKk2cxQKD_cDk63SSTCxNY87GgvdwvvgmmpxPqmeRXTtmdkLNoVIrCEgJ-mYYiG5G3VXJMzxlGbFhLIiQdu7xB_cHgFcLBuwj14hI3K1CIfDaWavlclsf9jZihnGTpIpGk9yCh-l5yuA\/s1600\/o365-no-reply-email-optim.jpg' \/\u003E\n\u003C\/noscript\u003E\n\n\u003Cp\u003E\n    A noreply email address comes in handy for many case studies, mainly for transactional purposes, being sent within a\n    website, an App, or any automated flows.\u003Cbr \/\u003E\n    Creating a simple noreply@ email account it's a no-brainer, but when required in creative marketing strategies,\n    won’t be enough.\u003C\/p\u003E\n\u003Cp\u003E\n    Let's take a look at this scenario\n\u003C\/p\u003E\n\n\u003Cdiv class=\"code\"\u003E\n    \u003Cpre\u003E\nAppName | SendFrom            | Info                             |\n--------+---------------------+----------------------------------+\nWebsite | news@7bis.eu.org    | News for site members            |\nWebsite | hi@7bis.eu.org      | Welcome emails, password reset   |\nCRM     | hi@7bis.eu.org      | Onboarding emails, password reset|\nCRM     | invoices@7bis.eu.org| Everything abouts invoices       |\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\u003Cp\u003EOur Website want's to send emails using two noreply\/unmonitored email accounts, our CRM App wants the same, while one\n    of our noreply@ email address (hi@7bis.eu.org) need to be used on both Apps. Does it look like a Many-to-Many\n    relationship? Yes, because it is, and this is the approach we will focus on from now on.\u003C\/p\u003E\n\n\u003Cp\u003EOf course, we can do it the easy way, create three Office 365 accounts, send authentication details for both\n    development teams (Website and CRM), and keep a note that if we ever need to reset the \"hi@7bis.eu.org\" account\n    password we shall announce both development teams about that. Ah, and you will have to deal with the \"unmonitored\"\n    status of the \"hi@7bis.eu.org\" Office 365 account\/email address.\n    Then you can stop here, no need for more reading.\u003C\/p\u003E\n\n\u003Ch2\u003ESending Office 365 emails using noreply\/unmonitored email addresses - the \"relationship way\"\u003C\/h2\u003E\n\u003Cdiv class=\"code\"\u003E\n    \u003Cpre\u003E\nAppName | LoginWith                 | SendAs              |\n--------+---------------------------+---------------------+\nWebsite | WebsiteMaster@7bis.eu.org | hi@7bis.eu.org      |\nCRM     | CrmMaster@7bis.eu.org     | hi@7bis.eu.org      |\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\u003Cp\u003E\n    Both Website and CRM Dev Teams will be able to send emails as (SendAs) \"hi@7bis.eu.org\", while both teams\n    authenticate (LoginWith) using individual credentials.\n    It's a One-to-Many relationship here, one noreply email address (hi@7bis.eu.org) to many teams (Website, CRM), or,\n    if you prefer, an \u003Cstrong\u003EOffice 365 Group\u003C\/strong\u003E with many \u003Cstrong\u003Eallowed senders\u003C\/strong\u003E.\n\u003C\/p\u003E\n\n\u003Cdiv class=\"code info\"\u003E\u003Ci class=\"material-icons large3em\"\u003Einfo\u003C\/i\u003E \"Unmonitored email address\" doesn't necessarily mean\n    \"no one check emails that come to that email address\".\n    It's (and that's the way it should, at the logic level at least) \"This email address won't receive any emails\".\n\u003C\/div\u003E\n\n\n\u003Cp\u003EWe will create one Office 365 group (Distribution list), using \"hi@7bis.eu.org\" as the associated email address, without members (this way, the email address won't receive\/distribute any incoming emails), with closed members join, hidden from GAL.\u003Cbr\/\u003E \nFurther, we can add management (\u003Cstrong\u003ESend As\u003C\/strong\u003E) Office 365 accounts (eg WebsiteMaster@7bis.eu.org and CrmMaster@7bis.eu.org) to it. Many-to-One.\u003C\/p\u003E\n\n\n\u003Ch2\u003ECreate a new Distribution list group\u003C\/h2\u003E\n\u003Cem\u003ERemember, no members allowed, no display in GAL\u003C\/em\u003E\n\n\u003Ctable align='center' cellpadding='0' cellspacing='7' class='tr-caption-container clear' width='725'\u003E\n    \u003Ctbody\u003E\n        \u003Ctr\u003E\n            \u003Ctd class='center'\u003E\n                \u003Camp-img alt='Create an O365 Distribution list group for noreply accounts' layout='responsive'\n                    lightbox=''\n                    src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEh-YD2LYfBdTyfPD0M_Pi05ePLDAX-T4A0HQR-82WoZ9MU_a3atJFvMbDKUqM9JxtFlRzrXxlrnD5TzWhPvkQc8p4aZTC06r3NqMFUB8SkALteYDyc8_H3cf0Kur9Dnrbvyz_paM-bJ4QlbnF1PPM6wFTtIk12fhb-RRYTm72utkNMUEzcn2jJEVujduw\/s1600\/o365-eac-add-group-optim.jpg'\n                    tabindex='0' width='725' height='343'\u003E\u003C\/amp-img\u003E\n                \u003Cbr \/\u003E\n            \u003C\/td\u003E\n        \u003C\/tr\u003E\n        \u003Ctr\u003E\n            \u003Ctd class='center'\u003E\n                \u003Camp-img alt='Create an O365 Distribution list group for noreply accounts' layout='responsive'\n                    lightbox=''\n                    src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEgSXCk4VgimBx4sKAJcHOHUDOGcSCbp5CG_7P91unK351jqCpNy1sPmPNx-cDURh85fFdEvtL3AeV9nze2WRQZJhV4onqtSBhTxLT1B3C0kxMZ-eISBJhyMyHfgTsQt0m35Xw-Cp8fg38lA0FqALVfZibHb6gqu_aFu22UTwMDBRAqN8KVgH9JWmlKBfg\/s1600\/o365-eac-add-group-no-reply-no-members-closed-join-optim.jpg'\n                    tabindex='0' width='725' height='470'\u003E\u003C\/amp-img\u003E\n            \u003C\/td\u003E\n        \u003C\/tr\u003E\n    \u003C\/tbody\u003E\n\u003C\/table\u003E\n\nAfter a while (sometime instantly, sometimes after a few seconds, maybe a minute), the new group will appear, ready to\nbe managed (click on the group name).\u003Cbr \/\u003E\n1.1 - Settings-\u003E[x]Hide this group from the global address list-\u003ESave\u003Cbr \/\u003E\n1.2 - Settings-\u003EEdit manage delegates-\u003E[search for, add it and set \"Send As\" permission for whatever Office 365 account\nneeded, eg WebsiteMaster@7bis.eu.org and CrmMaster@7bis.eu.org in our case].\n\u003Ctable align='center' cellpadding='0' cellspacing='7' class='tr-caption-container clear' width='725'\u003E\n    \u003Ctbody\u003E\n        \u003Ctr\u003E\n            \u003Ctd class='center'\u003E\n                \u003Camp-img alt='O365 Distribution list group Send As delegation' layout='responsive'\n                    lightbox=''\n                    src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhHw1YCoaWKCIJbI4xuTIjcsBOh0OQch3cw6dhLgm3IWvhOhg5tzKlNcj1jyEN_U724OyXesXbFPFKXRtgMP-Xw5LbT9hlyseMAFPZ_qV0ufuodfvSdhgvg23lUI7ubhYY5IZwthFBnb0LFBDeDQZaB2b_yD0vk1r0kuIWTbxjnJMGfOj_PzVfzcFZMDA\/s1600\/o365-eac-group-delegates-step2-optim.jpg'\n                    tabindex='0' width='725' height='450'\u003E\u003C\/amp-img\u003E\n            \u003C\/td\u003E\n        \u003C\/tr\u003E\n        \u003Ctr\u003E\n            \u003Ctd class='tr-caption'\u003EAccount not found\/not yet created? Go ahead and create one (in Microsoft 365 admin\n                center - MAC). If found, check the SMTP settings of this account\u003C\/td\u003E\n        \u003C\/tr\u003E\n    \u003C\/tbody\u003E\n\u003C\/table\u003E\n\n\u003Ch2\u003ECreate an Office 365 account, set SMTP access\u003C\/h2\u003E\n\u003Cp\u003ENote that to benefit from the SMTP protocol (that's it, no-reply\/transactional emails are usually sent by Apps using\n    the SMTP protocol), these accounts (eg WebsiteMaster@7bis.eu.org and CrmMaster@7bis.eu.org in our case) needs to be\n    licensed with an Exchange Online Plan \/ Exchange Online Kiosk (have a mailbox attached and SMTP enabled)\u003C\/p\u003E\n\n\u003Ctable align='center' cellpadding='0' cellspacing='7' class='tr-caption-container clear' width='725'\u003E\n    \u003Ctbody\u003E\n        \u003Ctr\u003E\n            \u003Ctd class='center'\u003E\n                \u003Camp-img alt='Create an O365 account for no-reply delegation' layout='responsive'\n                    lightbox=''\n                    src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEgJ8l7AA22GL5ut5eow7p-8JO1Zi3HBvfY3amsQFUL6DDMvcmw6my_zdpr0z9eAxEBz3zzMvMLjdJM-ayuq0N1RfFFA5Rb-bxM_2MlVpHBIbRYESuEgltcPkTnWYYkdIMhWHeu4RQLC-3wjB1iVbDkrOioilyItsCjkrzoAhUIchL3jjx7PgpGRqhLdPA\/s1600\/o365-admin-create-account-optim.jpg'\n                    tabindex='0' width='725' height='475'\u003E\u003C\/amp-img\u003E\n                \u003Cbr \/\u003E\n            \u003C\/td\u003E\n        \u003C\/tr\u003E\n        \u003Ctr\u003E\n            \u003Ctd class='center'\u003E\n                \u003Camp-img alt='Create an O365 Distribution list group for noreply accounts' layout='responsive'\n                    lightbox=''\n                    src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEgqheDzeA_bSCCXmfhNobo_4-CNQWRGKLO2wP6GNcMHfXrdblHIycSwofrpdeRYhcWjaAZ6-_vUiOwgS1mLqHe08zqiu0rM7PCUVUm_nLIyVv0mITKKeWntvXHnI8jRsO9N9U0jxE7MEgglWB1NpWgL5--bxpjCGmXCGwTzpJBgEkJtnIrb5WHv6D1V_A\/s1600\/o365-admin-create-account-asign-exchange-license-optim.jpg'\n                    tabindex='0' width='725' height='520'\u003E\u003C\/amp-img\u003E\n                \u003Cbr \/\u003E\n            \u003C\/td\u003E\n        \u003C\/tr\u003E\n        \u003Ctr\u003E\n            \u003Ctd class='center'\u003E\n                \u003Camp-img alt='Create an O365 Distribution list group for noreply accounts' layout='responsive'\n                    lightbox=''\n                    src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhC96jaMUdbA4E00_UuqNV8ejMK_aEt0sp02jLtKHr4w3-KDsftm5mfLaeDsKwHqV5C5C3U7Bw9kn8fh9I5UFkjgiumQoomuet42lPgr_1-0kp_d0_nL6LEDFhiybv19P3QVa2T7HLTlQa4f1iwFFxfOpFj7y3LTNk4hCPDsK0h-a_P3CbIg31fiMw9xQ\/s1600\/o365-admin-account-smtp-optim.jpg'\n                    tabindex='0' width='725' height='514'\u003E\u003C\/amp-img\u003E\n            \u003C\/td\u003E\n        \u003C\/tr\u003E\n        \u003Ctr\u003E\n            \u003Ctd class='tr-caption'\u003E[x]Authenticated SMTP, uncheck\/check other apps as per need\u003C\/td\u003E\n        \u003C\/tr\u003E\n    \u003C\/tbody\u003E\n\u003C\/table\u003E\n\n\n\nNow, you can go back to EAC -\u003E hi@7bis.eu.org group and add (as Send As delegate) the newly created\/SMTP enabled\nWebsiteMaster@7bis.eu.org account.\n\n\u003Cp\u003EThat's all, now you can create as many No-Reply distribution groups and delegate (for each group) who can send emails\n    using the No-Reply group email address.\u003C\/p\u003E\n\n\u003Cp\u003ELet's take a test drive too, we will authenticate using the WebsiteMaster@7bis.eu.org account and send emails using\n    hi@7bis.eu.org.\u003Cbr \/\u003E\n    The test was conducted using the PowerShell Send-MailMessage cmdlet (any programming language that speaks SMTP will work)\n    and the result is ...\u003C\/p\u003E\n\n\u003Ctable align='center' cellpadding='0' cellspacing='7' class='tr-caption-container clear' width='725'\u003E\n    \u003Ctbody\u003E\n        \u003Ctr\u003E\n            \u003Ctd class='center'\u003E\n                \u003Camp-img alt='Office 365 and No-Reply\/unmonitored email addresses management and usage' layout='responsive'\n                    lightbox=''\n                    src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEiHPeKmwtzEHWizBiUko64d8pdT7_LHLA04r8yVAbErEmO-g1HznzxtaA8y89Ka-r9w0YUD_y9fYTvF_puG-d9XC4wqBpnIWVRPWicZdrqJllpD3iyg2AYmTT_VL6lrS5oGkIksjkOVwTgtYWY8BWQgXgi6hS9O-_CUXUOTKGByoLFPnxpLim4MxjrJAw\/s1600\/no-reply-test-result-optim.jpg'\n                    tabindex='0' width='725' height='468'\u003E\u003C\/amp-img\u003E\n            \u003C\/td\u003E\n        \u003C\/tr\u003E\n        \u003Ctr\u003E\n            \u003Ctd class='tr-caption'\u003EThe correspondent received the email from \"hi@7bis.eu.org\"\u003C\/td\u003E\n        \u003C\/tr\u003E\n    \u003C\/tbody\u003E\n\u003C\/table\u003E\n\n\u003Cp\u003E\u003C\/p\u003E"},"link":[{"rel":"edit","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/5457090087843652860?v=2"},{"rel":"self","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/5457090087843652860?v=2"},{"rel":"alternate","type":"text/html","href":"https:\/\/www.b247.eu.org\/2022\/11\/office365-noreply-email-addresses.html","title":"Office 365 and No-Reply\/unmonitored email addresses management and usage"}],"author":[{"name":{"$t":"b247.eu.org"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/05580857583369963260"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"32","height":"32","src":"\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhCOFmryd_Wq7VuVeGmjJ4NXGLdVWFzH6sMG0LOgTugMQaZvlG44DsimJ7u7mhOuWfM5kC1eF9YAieX1YdqiWauCF_Imxv6HXrqaQIiyG5-52rceHYo9fYFhENwp9dKy2g\/s113\/logo600px.png"}}],"media$thumbnail":{"xmlns$media":"http://search.yahoo.com/mrss/","url":"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEjzw7bRJzvlIFt4scu0hqfeqVkF_29uZxGO_I3Ald6tupbIvPKKk2cxQKD_cDk63SSTCxNY87GgvdwvvgmmpxPqmeRXTtmdkLNoVIrCEgJ-mYYiG5G3VXJMzxlGbFhLIiQdu7xB_cHgFcLBuwj14hI3K1CIfDaWavlclsf9jZihnGTpIpGk9yCh-l5yuA\/s72-c\/o365-no-reply-email-optim.jpg","height":"72","width":"72"}},{"gd$etag":"W/\"C08ARn0yfyp7ImA9XxJaFUU.\"","id":{"$t":"tag:blogger.com,1999:blog-6816195014656817423.post-2631736023891351863"},"published":{"$t":"2022-04-10T02:10:00.001-07:00"},"updated":{"$t":"2022-04-10T02:10:47.397-07:00"},"app$edited":{"xmlns$app":"http://www.w3.org/2007/app","$t":"2022-04-10T02:10:47.397-07:00"},"category":[{"scheme":"http://www.blogger.com/atom/ns#","term":"PostgreSQL"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Web Development"}],"title":{"$t":"PostgreSQL, custom functions to generate random EAN13 and UUID (v4)"},"content":{"type":"html","$t":"\u003Cnoscript\u003E\n\u003Cimg alt='pgadmin3 with support for PostgreSQL 10,11,12' width='856' height='397' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEg-cZV2jI8BnXmTSeQ0hM4v2nI-Gbt4C_1O48fx3hp0CsHwBPy39M4knlCm5SYfYzODOfKTjbRHYU8-YT5F4tvsojtfVK2wv1xlZvosP2BCkLUZNY0u-F-LZywpzfHu35K9Gxoa7JhebsR7yZu-K06IZRooJsyV2-XvVBwGWhgO-BwR-6fLxzE1Yh2qrw\/s1600\/postgres-generate-ean13-uuid-random.jpg' \/\u003E\n\u003C\/noscript\u003E\n\n\n\u003Cp\u003EWhen dealing with a Postgres database that stores internal products\/assets, there is a chance that we will need either a unique EAN13 code for each or a UUID (Universal Unique Identifier), many times both of them.\u003C\/p\u003E\n\n\u003Cp\u003EFor dealing with EAN13 (or alike data types) there is the \"isn\" module.\u003Cbr \/\u003E\nFor dealing with UUIDs there are the \"pgcrypto\" and \"uuid-ossp\" modules.\u003C\/p\u003E\n\n\u003Cp\u003EIf, whatever the reason, installing the aforementioned modules is a no go, then, for generating valid random EAN13 and UUID codes, the following Postgres custom functions will do the job.\u003C\/p\u003E\n\n\n\u003Ch2\u003EPostgres custom function to generate random, valid EAN13 codes\u003C\/h2\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003ECREATE OR REPLACE FUNCTION gen_ean13_random()\n  RETURNS text AS\n$$\ndeclare ean13 text; check_digit integer;\nbegin\n\t\n\tean13 = (SELECT string_agg(to_hex(width_bucket(random(), 0, 1, 10)-1) ,'') FROM generate_series(1, 12));\n\tcheck_digit = (SELECT esum-mod(esum,10)+10-esum - CASE WHEN mod(esum,10) = 0 THEN 10 ELSE 0 END as ecd from (select sum( case when mod(row_number,2) = 0 THEN e::int * 3 ELSE e::int END) as esum from (select e, row_number() over() from  unnest(regexp_split_to_array(ean13,'')) e) q) q1);\n\tean13 = ean13||check_digit::TEXT;\n\t\n\treturn ean13;\nend;\n$$\nLANGUAGE plpgsql IMMUTABLE;\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\u003Ch2\u003EPostgres custom function to generate random, valid UUIDs\u003C\/h2\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003ECREATE OR REPLACE FUNCTION gen_uuid_random()\n  RETURNS uuid AS\n$$\n    SELECT string_agg(lpad(to_hex(width_bucket(random(), 0, 1, 256)-1),2,'0') ,'')::uuid FROM generate_series(1, 16);\n$$\n  LANGUAGE sql IMMUTABLE;\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\u003Cp\u003EUse them whenever you need, on-demand, or better, to automate things, within table triggers.\u003C\/p\u003E"},"link":[{"rel":"edit","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/2631736023891351863?v=2"},{"rel":"self","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/2631736023891351863?v=2"},{"rel":"alternate","type":"text/html","href":"https:\/\/www.b247.eu.org\/2022\/04\/postgres-random-ean13-uuid.html","title":"PostgreSQL, custom functions to generate random EAN13 and UUID (v4)"}],"author":[{"name":{"$t":"b247.eu.org"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/05580857583369963260"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"32","height":"32","src":"\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhCOFmryd_Wq7VuVeGmjJ4NXGLdVWFzH6sMG0LOgTugMQaZvlG44DsimJ7u7mhOuWfM5kC1eF9YAieX1YdqiWauCF_Imxv6HXrqaQIiyG5-52rceHYo9fYFhENwp9dKy2g\/s113\/logo600px.png"}}],"media$thumbnail":{"xmlns$media":"http://search.yahoo.com/mrss/","url":"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEg-cZV2jI8BnXmTSeQ0hM4v2nI-Gbt4C_1O48fx3hp0CsHwBPy39M4knlCm5SYfYzODOfKTjbRHYU8-YT5F4tvsojtfVK2wv1xlZvosP2BCkLUZNY0u-F-LZywpzfHu35K9Gxoa7JhebsR7yZu-K06IZRooJsyV2-XvVBwGWhgO-BwR-6fLxzE1Yh2qrw\/s72-c\/postgres-generate-ean13-uuid-random.jpg","height":"72","width":"72"}},{"gd$etag":"W/\"A0UHQnY6eSp7ImA9Xh5WEUs.\"","id":{"$t":"tag:blogger.com,1999:blog-6816195014656817423.post-2433063746927378238"},"published":{"$t":"2021-02-07T05:03:00.002-08:00"},"updated":{"$t":"2026-07-27T09:20:33.811-07:00"},"app$edited":{"xmlns$app":"http://www.w3.org/2007/app","$t":"2026-07-27T09:20:33.811-07:00"},"category":[{"scheme":"http://www.blogger.com/atom/ns#","term":"Linux"},{"scheme":"http://www.blogger.com/atom/ns#","term":"PostgreSQL"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Windows"}],"title":{"$t":"pgAdmin3 (III) with support for PostgreSQL 10,11,12,13"},"content":{"type":"html","$t":"\u003Cnoscript\u003E\n\u003Cimg alt='pgadmin3 with support for PostgreSQL 10,11,12' width='856' height='397' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEgkAm42mc53eDOhTnY-J5E2Z-lBLs_u1rFS0s-SIbLHW8m4hh3J99Z1XNFrqccEBCQ-fzwc5YEhcAwHsXPhg3m5ocKAPSUr31CBySaSxE3UHYDM7phtnqn9jgqI-qlh2jrcaSfadIoDUPNA\/s0\/database-elephant-blue-pgadmin.jpg' \/\u003E\n\u003C\/noscript\u003E\n\n\u003Cp\u003EpgAdmin is a free software used for PostgreSQL database management.\u003C\/p\u003E \n\u003Cp\u003EAs you may know, starting with pgAdmin 4, which is a WebApp, the former pgAdmin3 loses support for managing PostgreSQL servers version greater than 9.5. The latest pgAdmin3 official release is 1.22 and can still be used for PostgreSQL 10, 11, 12, 13, but with some missing features (ex failures to display the list of stored functions) and some strange errors.\u003Cbr\/\u003E\nBut, thanks to the Open Source world, the pgAdmin3 it's not dead yet. It's quite alive.\u003C\/p\u003E \n\n\u003Ch2\u003ECompile and install a patched pgAdmin 3 with support for PostgreSQL 10,11,12,13 - Ubuntu Desktop 20.04 LTS\u003C\/h2\u003E\n\u003Cp\u003EAn open-source version of a patched pgAdmin3 can be found here \u003Ca href=\"https:\/\/github.com\/allentc\/pgadmin3-lts.git\" target=\"_blank\" rel=\"nofollow noreferrer noopener\"\u003Ehttps:\/\/github.com\/allentc\/pgadmin3-lts.git\u003C\/a\u003E, a repo that we will use to compile and install pgAdmin3-LTS (v1.23b,BigSQL,forked) on our Ubuntu Desktop machine.\u003C\/p\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003Esudo apt install postgresql-server-dev-12 libwxgtk-media3.0-gtk3-dev\ngit clone https:\/\/github.com\/allentc\/pgadmin3-lts.git\ncd pgadmin3-lts\nbash bootstrap\n.\/configure --prefix=\/usr\/local --with-pgsql=\/usr\/lib\/postgresql\/12 --with-libgcrypt CFLAGS=-fPIC CXXFLAGS=-fPIC --with-wx-version=3.0\nmake\nsudo make install\n\u003C\/pre\u003E\u003C\/div\u003E\n\u003Cp\u003EIf everything worked as expected, create a Gnome application launcher for quick access: \u003Ccode\u003Esudo nano \/usr\/share\/applications\/pgadmin3.desktop\u003C\/code\u003E with bellow content, save it (Ctrl+X) and reload Gnome afterall (Alt+F2, r)\u003C\/p\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003E[Desktop Entry]\nName=pgAdmin3\nExec=pgadmin3\nIcon=\/var\/lib\/app-info\/icons\/ubuntu-focal-universe\/64x64\/pgadmin3_pgadmin3.png\nType=Application\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\u003Cp\u003E\u0026nbsp;\u003C\/p\u003E\n\u003Ctable align='center' cellpadding='0' cellspacing='5' class='tr-caption-container clear' width='450'\u003E\u003Ctbody\u003E\n\u003Ctr\u003E\u003Ctd class='center'\u003E\n\u003Camp-img alt='pgadmin3 with support for PostgreSQL 10,11,12,13' layout='responsive' lightbox='' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEga721zRV93GzcgtxDf_fZhsNS9Q4TQvyLhAtHBzDA6zVaDagNB90T-25pcm-PRHEOHprC6m6MMqmFYMNRt0aBhYVpsJ8_wT9ZIlLnwrKwjNA074i3wVfIskqfoV06uYcUynKvzv-gAgTtI\/s0\/pgadmin-3-lts-bigsql-allentc.jpg' tabindex='0' width='489' height='268'\u003E\u003C\/amp-img\u003E\n\u003Cbr \/\u003E\u003C\/td\u003E\u003C\/tr\u003E\n\u003C\/tbody\u003E\u003C\/table\u003E\n\n\u003Ch2\u003EInstall a patched pgAdmin 3 with support for PostgreSQL 10,11,12,13 - Windows and other Debian based Linux distros\u003C\/h2\u003E\n\u003Cp\u003EFor those who are not comfortable compiling software (yes, the aforementioned repo can be used to compile pgAdmin3 not only for Linux but also for Windows and macOS), I've found Victor Spirin from \u003Ca href=\"http:\/\/mobile-n.com\" target=\"_blank\" rel=\"nofollow noreferrer noopener\"\u003Ehttp:\/\/mobile-n.com\u003C\/a\u003E who is keeping alive up to date pgAdmin3 installers, for Windows and Debian, here: \u003Ca href=\"https:\/\/vvs.ru\/pg\/index-en.html\" target=\"_blank\" rel=\"nofollow noreferrer noopener\"\u003Ehttps:\/\/vvs.ru\/pg\/index-en.html\u003C\/a\u003E. Thank you, Victor.\u003C\/p\u003E\n\n\u003C!-- \u003Cdiv class=\"code info\"\u003E\u003Ci class=\"material-icons large3em\"\u003Einfo\u003C\/i\u003E Here you can find a backup copy of Victor's \u003Ca href=\"https:\/\/github.b247.eu.org\/download\/pgadmin3-1.27.1.msi\" target=\"_blank\" rel=\"me nofollow noreferrer noopener\"\u003EWindows\u003C\/a\u003E and \u003Ca href=\"https:\/\/github.b247.eu.org\/download\/pgadmin3_1.27.1b-1_amd64.deb\" target=\"_blank\" rel=\"me nofollow noreferrer noopener\"\u003EDebian\u003C\/a\u003E pgAdmin3 installers\u003C\/div\u003E --\u003E\n\n\u003Cp\u003EThat's it, pgAdmin3 at least till the EOL of PostgreSQL 13\u003C\/p\u003E\n"},"link":[{"rel":"edit","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/2433063746927378238?v=2"},{"rel":"self","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/2433063746927378238?v=2"},{"rel":"alternate","type":"text/html","href":"https:\/\/www.b247.eu.org\/2021\/02\/pgadmin-3-postgres-11-12-12-support.html","title":"pgAdmin3 (III) with support for PostgreSQL 10,11,12,13"}],"author":[{"name":{"$t":"b247.eu.org"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/05580857583369963260"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"32","height":"32","src":"\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhCOFmryd_Wq7VuVeGmjJ4NXGLdVWFzH6sMG0LOgTugMQaZvlG44DsimJ7u7mhOuWfM5kC1eF9YAieX1YdqiWauCF_Imxv6HXrqaQIiyG5-52rceHYo9fYFhENwp9dKy2g\/s113\/logo600px.png"}}],"media$thumbnail":{"xmlns$media":"http://search.yahoo.com/mrss/","url":"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEgkAm42mc53eDOhTnY-J5E2Z-lBLs_u1rFS0s-SIbLHW8m4hh3J99Z1XNFrqccEBCQ-fzwc5YEhcAwHsXPhg3m5ocKAPSUr31CBySaSxE3UHYDM7phtnqn9jgqI-qlh2jrcaSfadIoDUPNA\/s72-c\/database-elephant-blue-pgadmin.jpg","height":"72","width":"72"}},{"gd$etag":"W/\"A0UHQnY6eSp7ImA9Xh5WEUs.\"","id":{"$t":"tag:blogger.com,1999:blog-6816195014656817423.post-3852219051087213844"},"published":{"$t":"2020-11-30T06:41:00.005-08:00"},"updated":{"$t":"2026-07-27T09:20:33.811-07:00"},"app$edited":{"xmlns$app":"http://www.w3.org/2007/app","$t":"2026-07-27T09:20:33.811-07:00"},"category":[{"scheme":"http://www.blogger.com/atom/ns#","term":"Encryption"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Linux"}],"title":{"$t":"Ubuntu, how to connect to IPsec\/IKEv2 VPNs via NetworkManager"},"content":{"type":"html","$t":"\u003Cnoscript\u003E\n\u003Cimg alt='php strong password generator, generate random strongs passwords' width='856' height='397' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhF_S5NHpU1AiWQJ6hwVLrEu4_FC4YBuB17zzn4F63a_1eERwZR8J118IcS4TseEmWxcb5EJwZfjjTKGK18tZbLT2lFIggPc-M3M-qaQrchInqRApA_PGS2XsyHmovuOsGoyyuOmAeBaDSf\/s0\/strongswan-ipsec-mschap-ikev2-856x397-optim.jpg' \/\u003E\n\u003C\/noscript\u003E\n\n\u003Cp\u003E\nVPN: Virtual Private Network\u003Cbr\/\u003E\nStrongswan: IPsec-based VPN\u003Cbr\/\u003E\nIKEv2: VPN encryption protocol\u003Cbr\/\u003E\nMS-CHAPv2 (MSCHAPv2): authentication protocol\n\u003C\/p\u003E\n\n\n\u003Cp\u003EVPNs can be used for various reasons: the need to securely connect to a company's local network resources; the need to bypass geolocation restrictions; to route our internet traffic through a secured gateway. Whatever the reason, we have to be able to spawn a tunnel for that (connection to a VPN provider\/server).\u003C\/p\u003E\n\n\u003Cdiv class=\"code info\"\u003E\u003Ci class=\"material-icons large3em\"\u003Einfo\u003C\/i\u003E A lot of VPN providers are using at least one or both OpenVPN or Strongswan IPsec VPN solutions to provide secure tunnels between a client and their servers.\u003C\/div\u003E\n\n\u003Ch2\u003EHow do we connect to these VPN providers using Ubuntu Linux\u003C\/h2\u003E\n\n\u003Cp\u003EWith OpenVPN everything is damn simple, install the OpenVPN NetworkManager plugin (\u003Ccode\u003Esudo apt install network-manager-openvpn\u003C\/code\u003E) then import an OpenVPN connection by double click on a .ovpn file (provided by the VPN provider). With \u003Cstrong\u003EStrongswan VPNs\u003C\/strong\u003E (configured with IKEv2 encryption and MSCHAPv2 authentication) things gets a little bit complicated ...\u003C\/p\u003E\n\n\u003Cp\u003E... it didn't work, always authentication failure notices. We can connect to the VPN using CLI, it work, except ... the CLI solution doesn't provide Gnome visual feedbacks. So I wrote a small shell script that wraps CLI connections (and NM ones), added it to the Argos Gnome Extension, and here we are, we can connect\/disconnect\/see the status of our CLI based IKEv2 IPsec-VPN connection, using the Gnome interface.\u003C\/p\u003E\n\n\u003Cp\u003E ... meanwhile, I didn't forget that there is a Network Manager extension that should work, and thanks to Tobias from Strongswan, who was kind enough to provide me direct assistance, I've managed to connect to my VPNs using the NetworkManager interface, even to \"use the connection only for resources on its network\".\u003C\/p\u003E\n\n\u003Ch2\u003EStep 1: compile strongswan from source\u003C\/h2\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003Ewget https:\/\/download.strongswan.org\/strongswan-5.9.10.tar.bz2\ntar -xf strongswan-5.9.10.tar.bz2\ncd strongswan-5.9.10\n.\/configure --sysconfdir=\/etc --prefix=\/usr --libexecdir=\/usr\/lib \\\n   --enable-aesni --enable-acert --disable-aes --disable-des --disable-md5 --disable-sha1 --disable-sha2 \\\n   --disable-fips-prf --disable-gmp --enable-openssl --enable-nm --enable-agent \\\n   --disable-eap-gtc --disable-eap-md5 --disable-eap-peap --enable-eap-mschapv2 --enable-eap-identity\nmake\nsudo make install\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\u003Cp\u003E\u003C\/p\u003E\n\u003Cdiv class=\"code info\"\u003E\u003Ci class=\"material-icons large3em\"\u003Einfo\u003C\/i\u003E Change the strongswan version \u003Ccode\u003E5.9.10\u003C\/code\u003E with one up to date at the time; remove the \u003Ccode\u003E--enable-aesni\u003C\/code\u003E flag if your CPU or kernel doesn't support AES-NI\u003C\/div\u003E\n\n\n\u003Ch2\u003EStep 2: compile strongswan network-manager plugin from source\u003C\/h2\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003Ewget https:\/\/download.strongswan.org\/NetworkManager\/NetworkManager-strongswan-1.6.0.tar.bz2\ntar -xf NetworkManager-strongswan-1.6.0.tar.bz2\ncd NetworkManager-strongswan-1.6.0\n.\/configure --sysconfdir=\/etc --prefix=\/usr --libexecdir=\/usr\/lib --without-libnm-glib\nmake\nsudo make install\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\n\u003Ch2\u003EStep 3: disable peap, md5, and gtc plugins for charon-nm (strongswan network-manager plugin)\u003C\/h2\u003E\nEdit \/etc\/strongswan.conf \u003Ccode\u003Esudo nano \/etc\/strongswan.conf\u003C\/code\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003Echaron {\n  load_modular = yes\n  plugins {\n    include strongswan.d\/charon\/*.conf\n  }\n}\n\ncharon-nm {\n  plugins {\n    eap-peap {\n      load = no\n    }\n    eap-md5 {\n      load = no\n    }\n    eap-gtc {\n      load = no\n    }\n  }\n}\n\ninclude strongswan.d\/*.conf\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\n\u003Cp\u003EAt this step we shall be able to establish connections to IPsec\/IKEv2 VPNs via NetworkManager.\u003C\/p\u003E\n\n\u003Ctable align='center' cellpadding='0' cellspacing='5' class='tr-caption-container clear' width='450'\u003E\u003Ctbody\u003E\n\u003Ctr\u003E\u003Ctd class='tr-caption'\u003Ehide.me VPN IPSec\/IKEv2 connection using NetworkManager\u003C\/td\u003E\u003C\/tr\u003E\n\u003Ctr\u003E\u003Ctd class='center'\u003E\n\u003Camp-img alt='hide.me ike v2 ipsec Networkmanager' layout='responsive' lightbox='' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhlKi47nkaBhHNy7wNnP4-De9UthD5_XYyyGFHa2YWdXQq574B9cVe_-mGMXc8wJ-TltGBH9Arx2vmVOyIP9b_4VXlS7eOJMieDoiy-UckiYmgJ2AJED_b9ISBxFt0EF1PVlad7ULLjCKG_\/s0\/ipsec-nm-optim.png' tabindex='0' width='632' height='858'\u003E\u003C\/amp-img\u003E\u003C\/td\u003E\u003C\/tr\u003E\n\u003Ctr\u003E\u003Ctd class='tr-caption'\u003E*pem from https:\/\/hide.me\/downloads\/hide.me.pem\u003C\/td\u003E\u003C\/tr\u003E\n\u003C\/tbody\u003E\u003C\/table\u003E\n\n\u003Ch2\u003EUsing a VPN connection only for resources on its network\u003C\/h2\u003E\n\u003Cp\u003EThe default \"Use this connection only for resources on its network\" checkbox (within the NM VPN setting IPv4 panel) does nothing. That's why I've initially written the extra steps 4-6 tutorial, but recently discovered the \"dispatcher\" options of the NetworkManager and decided to give it a try, and it works.\u003C\/p\u003E\n\u003Cp\u003EHow to make it work: just install ipcalc \u003Ccode\u003Esudo apt install ipcalc\u003C\/code\u003E and add the following code snippet to the \u003Ccode\u003E\/etc\/NetworkManager\/dispatcher.d\/01-ifupdown\u003C\/code\u003E dispatcher script\u003C\/p\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003E# VPN: use this connection only for resources on its network\nif [ \"$2\" = \"vpn-up\" ]; then\n   CONN_INTERNAL_ONLY=$(grep 'never-default' $CONNECTION_FILENAME)\n   if [ -n \"$CONN_INTERNAL_ONLY\" ]; then\n      VPN_IP4_ADDRESS=$(echo $VPN_IP4_ADDRESS_0 | sed -n 's\/\\([0-9\\.]*\\).*\/\\1\/p')\n      VPN_IP4_NETWORK=$(ipcalc $VPN_IP4_ADDRESS | grep Network | awk '{print $2}')\n      ip rule add not from all to $VPN_IP4_NETWORK table main priority 219\n   fi\nfi\nif [ \"$2\" = \"vpn-down\" ]; then\n   ip rule del pref 219\nfi\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\u003Ch2\u003E[UPDATE] Further steps (4-6) still work and offer a nice and seamless Gnome integration, but they are no longer necessary\u003C\/h2\u003E\n\u003C!-- \u003Cp\u003E\u003Cbr\/\u003E\u003C\/p\u003E\n\u003Cdiv class=\"code info\"\u003E\u003Ci class=\"material-icons large3em\"\u003Einfo\u003C\/i\u003E Further steps (4-6) still work and offer seamless Gnome integration, but they are no longer necessary.\u003C\/div\u003E --\u003E\n\u003Ctable align='center' cellpadding='0' cellspacing='5' class='tr-caption-container clear' width='650'\u003E\u003Ctbody\u003E\n    \u003Ctr\u003E\u003Ctd class='tr-caption'\u003EGnome VPN custom extension\u003C\/td\u003E\u003C\/tr\u003E\n    \u003Ctr\u003E\u003Ctd class='center'\u003E\n    \u003Camp-img alt='Gnome VPN extension, IPsec, IKEv2, MS-CHAP, MSCHAPv2' layout='responsive' lightbox='' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEjyhTI1wLOOa6X0AsKOTC93sgnqp_CL3gbtrtmspPE-HcjhALEZCatSEM8vcCTC7VXdFUfgFdrgTEi7yWrPm3ZrfA67QyfRntu4cR6Kr-5j0s_p0X6JuSo1tdCrH9M3v2buLUKIThzY0_Be\/s0\/ipsec-vpn-gnome-extension.jpg' tabindex='0' width='826' height='383'\u003E\u003C\/amp-img\u003E\n    \u003Cbr \/\u003E\u003C\/td\u003E\u003C\/tr\u003E\n    \u003C\/tbody\u003E\u003C\/table\u003E\n\n\u003Ch2\u003EStep 4-CLI: copy VPN server or CA provided certificate to \/etc\/ssl\/certs\u003C\/h2\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003Esudo cp \/path\/to\/vpn-server.pem \/etc\/ssl\/certs\/\nsudo rm -r \/etc\/ipsec.d\/cacerts\/\nsudo ln -s \/etc\/ssl\/certs \/etc\/ipsec.d\/cacerts\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\u003Ch2\u003EStep 5-CLI: declare your VPN connections on \/etc\/ipsec.conf\u003C\/h2\u003E\nEdit ipsec.conf \u003Ccode\u003Esudo nano \/etc\/ipsec.conf\u003C\/code\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003Econn road-warrior-base\n    keyexchange=ike\n    fragmentation=yes\n    dpdaction=clear\n    dpdtimeout=90s\n    dpddelay=30s\n\nconn contoso.localnet           #adjust this to better reflect the VPN connection name\n    also=road-warrior-base\n    eap_identity=catalin        #adjust this as required\n    leftauth=eap-mschapv2\n    left=%defaultroute\n    leftsourceip=%config\n    right=vpn.contoso.net       #adjust this as required\n    rightsubnet=10.10.1.0\/24    #adjust this as required (only trafic to 10.10.1.0\/24 network is routed via this tunnel)\n    type=tunnel\n    auto=add\n\nconn hide.me                    #this is a real VPN provider that works on both CLI and NM\n    also=road-warrior-base\n    eap_identity=b247           #adjust this as required\n    leftauth=eap-mschapv2\n    left=%defaultroute\n    leftsourceip=%config\n    right=free-nl.hide.me       #for a free hide.me VPN account\n    rightauth=pubkey\n    rightsubnet=0.0.0.0\/0       #all trafic is routed via this tunnel\n    rightid=\"C=MY, L=Labuan, O=eVenture Limited, CN=*.hide.me\"\n    type=tunnel\n    auto=add\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\u003Cp\u003E\u003C\/p\u003E\n\u003Cdiv class=\"code info\"\u003E\u003Ci class=\"material-icons large3em\"\u003Einfo\u003C\/i\u003E We have here two connections (road-warrior-base it's a helper one) contoso.localnet connects to our company and is used only to access resources inside our company network (eg 10.10.1.0\/24); hide.me is another connection, this time for the hide.me VPN provider. hide.me is used for all our internet traffic (0.0.0.0\/0). hide.me is working too with NetworkManager - configured with this \u003Ca href=\"https:\/\/hide.me\/downloads\/hide.me.pem\" target=\"_blank\" rel=\"nofollow noreferrer noopener\"\u003Ehttps:\/\/hide.me\/downloads\/hide.me.pem\u003C\/a\u003E server certificate.\u003C\/div\u003E\n\n\u003Ch2\u003EStep 6-CLI: declare your EAP passwords on \/etc\/ipsec.secrets\u003C\/h2\u003E\nEdit ipsec.secrets \u003Ccode\u003Esudo nano \/etc\/ipsec.secrets\u003C\/code\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003Econtosousername : EAP \"the password for contoso.localnet\"\nhidemeusername : EAP \"the password for hide.me\"\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\u003Cp\u003E\u003C\/p\u003E\n\u003Cdiv class=\"code info\"\u003E\u003Ci class=\"material-icons large3em\"\u003Einfo\u003C\/i\u003E Adjust usernames and passwords as required. Keep password inside double quotes. Restart ipsec (the charon wrapper) \u003Ccode\u003Esudo ipsec restart\u003C\/code\u003E and try to connect to any of ipsec.conf declared VPNs, using CLI (\u003Ccode\u003Esudo ipsec up contoso.localnet\u003C\/code\u003E, \u003Ccode\u003Esudo ipsec up hide.me\u003C\/code\u003E).\u003C\/div\u003E\n\n\n\u003Ch2\u003EThe Gnome VPN custom extension\u003C\/h2\u003E\n\u003Cp\u003EFirst we will need to install the argos gnome extension (https:\/\/extensions.gnome.org\/extension\/1176\/argos\/), using either Firefox or Chromium\/Chrome, then we have to patch it for Gnome 3.36+ compatibility\u003C\/p\u003E\n\n\u003Cdiv class=\"code info\"\u003E\u003Ci class=\"material-icons large3em\"\u003Einfo\u003C\/i\u003E I applied the patch because of an incompatibility issue at the time (menu toggle not working on Gnome 3.36+). The patch may not be required for future extension releases.\u003C\/div\u003E\n\n\u003Cp\u003E\u003C\/p\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003E\ngit clone --single-branch --branch gnome-3.36 https:\/\/github.com\/rammie\/argos.git \u0026\u0026 cp -r argos\/argos@pew.worldwidemann.com \/home\/$USER\/.local\/share\/gnome-shell\/extensions\/\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\u003Cp\u003ECreate vpn.5s.sh \u003Ccode\u003Enano \/home\/$USER\/.config\/argos\/vpn.5s.sh\u003C\/code\u003E\u003C\/p\u003E\n\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003E#!\/usr\/bin\/env bash\n\nSTARTER_VPNS=( $(cat \/etc\/ipsec.conf | grep conn | awk '{print $2}') ) # get a list of starter vpns\nBLACKLISTED_STARTER_VPNS=( \"road-warrior-base\" ) # blacklist (remove from the menu)\n\nNM_VPNS=( $(nmcli con show | grep vpn | awk '{print $1}') ) # get a list of NM vpns\n\nfor i in \"${BLACKLISTED_STARTER_VPNS[@]}\"; do\n     STARTER_VPNS=(${STARTER_VPNS[@]\/\/*$i*})\ndone\n\nVPN_IP=$(ip r show table 220 | grep 'src' | awk '{print $9}') # get VPN IP from routing table\nNM_VPN_NAME=$(nmcli con show --active | grep -i vpn | awk '{print $1}') # get VPN conn name from nmcli\nSTARTER_VPN_ACTIVE=$(cat \/tmp\/savpn.tmp) # use a temporary file to store active vpn from starter\n\nVPN_ICON=\"network-vpn\"\nif [[ $NM_VPN_NAME ]]; then\n    VPN_INFO=\"$NM_VPN_NAME $VPN_IP | iconName=$VPN_ICON\"\nelse\n    VPN_INFO=\"$STARTER_VPN_ACTIVE $VPN_IP | iconName=$VPN_ICON\"\nfi\n\n\necho ${VPN_INFO}\necho \"---\"\n\nfor i in \"${STARTER_VPNS[@]}\"\ndo\n    if [[ $STARTER_VPN_ACTIVE == $i ]]; then\n        action=\"cat \/dev\/null \u003E \/tmp\/savpn.tmp \u0026\u0026 pkexec ipsec down $i\"\n        icon=network-wired\n    else\n        if [[ -z $STARTER_VPN_ACTIVE ]]; then\n            action=\"echo $i \u003E \/tmp\/savpn.tmp \u0026\u0026 pkexec ipsec up $i\"\n        else\n            action=\n        fi\n        icon=\n    fi\n    echo \"$i | iconName=$icon bash='$action' terminal=false refresh=true\"\n\ndone\n\nfor i in \"${NM_VPNS[@]}\"\ndo\n    if [[ $NM_VPN_NAME == $i ]]; then\n        action=\"nmcli con down $i\"\n        icon=network-wired\n    else\n        if [[ $NM_VPN_NAME ]]; then\n            action=\"nmcli con down $NM_VPN_NAME \u0026\u0026 nmcli con up $i\"\n        else\n            action=\"nmcli con up $i\"\n        fi\n        icon=\n    fi\n    echo \"$i | iconName=$icon bash='$action' terminal=false refresh=true\"\n\ndone\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\n\u003C!-- \u003Ctable align='center' cellpadding='0' cellspacing='5' class='tr-caption-container clear' width='650'\u003E\u003Ctbody\u003E\n\u003Ctr\u003E\u003Ctd class='tr-caption'\u003EThe Gnome VPN extension\u003C\/td\u003E\u003C\/tr\u003E\n\u003Ctr\u003E\u003Ctd class='center'\u003E\n\u003Camp-img alt='Gnome VPN extension, IPsec, IKEv2, MS-CHAP, MSCHAPv2' layout='responsive' lightbox='' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEjyhTI1wLOOa6X0AsKOTC93sgnqp_CL3gbtrtmspPE-HcjhALEZCatSEM8vcCTC7VXdFUfgFdrgTEi7yWrPm3ZrfA67QyfRntu4cR6Kr-5j0s_p0X6JuSo1tdCrH9M3v2buLUKIThzY0_Be\/s0\/ipsec-vpn-gnome-extension.jpg' tabindex='0' width='826' height='383'\u003E\u003C\/amp-img\u003E\n\u003Cbr \/\u003E\u003C\/td\u003E\u003C\/tr\u003E\n\u003C\/tbody\u003E\u003C\/table\u003E --\u003E\n\n\u003Cp\u003EIt wasn't easy to achieve this functionality, but I need it (mainly because of the \"work from home\"  - COVID-19 prevention strategies). It works as expected, I'm using this extension to connect\/disconnect\/see the connection status for both NM managed VPNs (charon-nm) and the charon managed ones (CLI).\u003C\/p\u003E\n\n\u003Cp\u003EThank You for reading this article.\u003C\/p\u003E"},"link":[{"rel":"edit","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/3852219051087213844?v=2"},{"rel":"self","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/3852219051087213844?v=2"},{"rel":"alternate","type":"text/html","href":"https:\/\/www.b247.eu.org\/2020\/11\/ubuntu-how-to-connect-to-strongswan-ipsec-based-vpns.html","title":"Ubuntu, how to connect to IPsec\/IKEv2 VPNs via NetworkManager"}],"author":[{"name":{"$t":"b247.eu.org"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/05580857583369963260"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"32","height":"32","src":"\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhCOFmryd_Wq7VuVeGmjJ4NXGLdVWFzH6sMG0LOgTugMQaZvlG44DsimJ7u7mhOuWfM5kC1eF9YAieX1YdqiWauCF_Imxv6HXrqaQIiyG5-52rceHYo9fYFhENwp9dKy2g\/s113\/logo600px.png"}}],"media$thumbnail":{"xmlns$media":"http://search.yahoo.com/mrss/","url":"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhF_S5NHpU1AiWQJ6hwVLrEu4_FC4YBuB17zzn4F63a_1eERwZR8J118IcS4TseEmWxcb5EJwZfjjTKGK18tZbLT2lFIggPc-M3M-qaQrchInqRApA_PGS2XsyHmovuOsGoyyuOmAeBaDSf\/s72-c\/strongswan-ipsec-mschap-ikev2-856x397-optim.jpg","height":"72","width":"72"}},{"gd$etag":"W/\"AkAGRn0yeip7ImA9Xh5WEUs.\"","id":{"$t":"tag:blogger.com,1999:blog-6816195014656817423.post-7407255131315436841"},"published":{"$t":"2020-11-28T04:55:00.007-08:00"},"updated":{"$t":"2026-07-27T09:12:07.392-07:00"},"app$edited":{"xmlns$app":"http://www.w3.org/2007/app","$t":"2026-07-27T09:12:07.392-07:00"},"category":[{"scheme":"http://www.blogger.com/atom/ns#","term":"Development"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Free Web"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Security"}],"title":{"$t":" A php strong password generator, with a pinch of salt"},"content":{"type":"html","$t":"\u003C!-- A php strong password generator, with a pinch of salt --\u003E\n\n\u003Cnoscript\u003E\n\u003Cimg alt='php strong password generator, generate random strongs passwords' width='856' height='397' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhZgYGG3rovD0xqfUqlTxzRmnL_LON59jLkpvvPIkWkukNASP2eX6lLKGn5LUEJlPAPD1N558vGYcP4ULWu0Uj7Tqn67k5YxX4_03WuJ2_s0rVeVWelnD4R7-InCAEzmZ2shpjjfpo5NUvC\/s0\/passwords-generator-php-856x397-optim.jpg' \/\u003E\n\u003C\/noscript\u003E\n\n\u003Cp\u003EWhen it comes to protecting web user accounts, the first step is to ask users for a strong password, one composed of 10-12 lowercased, uppercased, numeric, and special characters.\u003C\/p\u003E\n\n\u003Cp\u003EThe length and complexity of a password have its importance not just because it may be hard to guess but mainly because of the resistance against brute force attacks (conducted on a compromised database - like one leaked from  123rf.com, or directly against login forms).\u003C\/p\u003E\n\n\u003Cp\u003ESo we ask for a strong password from users, but what if we want to help them with a random password that respects the same password strength rules? \u003C\/p\u003E\n\n\u003Cp\u003EHere comes in handy the bellow PHP function, able to generate (compute) a strong random password that respects the aforementioned algorithm (12 lowercased, uppercased, numeric, and special characters).\u003C\/p\u003E\n\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003Efunction generate_password( $length = 12 ) {\n    $password = null;\n    $charset = array('abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', '0123456789', '@.#!$%_*~');\n    $csl = 4; $max_cps = $length \u003E= 12 ? 9 : 9 - 12 + $length;\n    $cps = array();\n    \n    while (true) {\n       for ($i=0; $i \u003C $csl; $i++) { \n          $cps[$i] = random_int(1, $max_cps);\n       }\n       if (array_sum($cps) == $length) {\n          break;\n       }\n       \n    }\n\n   foreach ($charset as $key =\u003E $val) {\n      for ( $i = 0; $i \u003C $cps[$key]; $i++ ) {\n         $password .= substr( $val, random_int( 0, strlen( $val ) - 1 ), 1 );\n      }\n   }\n   \n   return str_shuffle($password);\n}\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\u003Cp\u003EThe function uses four sets of lowercased, uppercased, numeric, and special characters, extracts a random string of a random length from each set until the compute of 12 (the $length) characters. And finally, it shuffles the already randomized result. The only secret here is how it computes the random length of characters to be used from each set.\u003C\/p\u003E\n\n\u003Cp\u003E\u003Cstrong\u003EUse it whenever you want or have to provide users with a strong random password.\u003C\/strong\u003E\u003C\/p\u003E\n\n\u003Cp\u003EFor a better security, beside a strong password, a web platform should always provide more security tools, like 2FA (two factor\/step auth) - in case of leaked passwords, fail2ban - against brute force login attempts, and passwords stored as hashed and salted with bcrypt, crypt blowfish, argon2 - against rainbow table attacks.\u003C\/p\u003E\n\n\u003Cp\u003EStay safe.\u003C\/p\u003E\n"},"link":[{"rel":"edit","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/7407255131315436841?v=2"},{"rel":"self","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/7407255131315436841?v=2"},{"rel":"alternate","type":"text/html","href":"https:\/\/www.b247.eu.org\/2020\/11\/a-php-strong-password-generator.html","title":" A php strong password generator, with a pinch of salt"}],"author":[{"name":{"$t":"b247.eu.org"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/05580857583369963260"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"32","height":"32","src":"\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhCOFmryd_Wq7VuVeGmjJ4NXGLdVWFzH6sMG0LOgTugMQaZvlG44DsimJ7u7mhOuWfM5kC1eF9YAieX1YdqiWauCF_Imxv6HXrqaQIiyG5-52rceHYo9fYFhENwp9dKy2g\/s113\/logo600px.png"}}],"media$thumbnail":{"xmlns$media":"http://search.yahoo.com/mrss/","url":"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhZgYGG3rovD0xqfUqlTxzRmnL_LON59jLkpvvPIkWkukNASP2eX6lLKGn5LUEJlPAPD1N558vGYcP4ULWu0Uj7Tqn67k5YxX4_03WuJ2_s0rVeVWelnD4R7-InCAEzmZ2shpjjfpo5NUvC\/s72-c\/passwords-generator-php-856x397-optim.jpg","height":"72","width":"72"}},{"gd$etag":"W/\"AkABQnk9cCp7ImA9Xh5WEUs.\"","id":{"$t":"tag:blogger.com,1999:blog-6816195014656817423.post-1447364845382184323"},"published":{"$t":"2020-07-12T05:18:00.000-07:00"},"updated":{"$t":"2026-07-27T09:12:33.768-07:00"},"app$edited":{"xmlns$app":"http://www.w3.org/2007/app","$t":"2026-07-27T09:12:33.768-07:00"},"category":[{"scheme":"http://www.blogger.com/atom/ns#","term":"Development"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Free Web"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Linux"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Web Hosting"}],"title":{"$t":"1 F1-micro google VM, your Linux server, production-ready playground"},"content":{"type":"html","$t":"\u003Cnoscript\u003E\n\u003Cimg alt='1 F1-micro google VM, your free Linux server, production-ready playground' width='856' height='397' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEis816Vpa1WGc0Uy52tLZ7yJLcJbqP6f_rtsCYMx0qI-zvvZaMPqbp0gJEUZbJTc4XHgdeG6n1muiGa4qINyXYocNl-09osNjTD5RvD2U7kcogC48RVMCbtllWKp5Z5YUtcHzGT3MOwSQEq\/s856\/gce-f1-vm.jpg' \/\u003E\n\u003C\/noscript\u003E\n\n\u003Cp\u003EUsing Google Cloud free\/always free products it has to offer we can create a small yet powerful free virtual machine (a Google Cloud Compute Engine product) to learn the basics of Linux administration or to leverage our skills into production. \u003C\/p\u003E\n\n\u003Ctable align='center' cellpadding='0' cellspacing='5' class='tr-caption-container clear' width='650'\u003E\u003Ctbody\u003E\n\u003Ctr\u003E\u003Ctd class='tr-caption'\u003Ecloud.google.com\/free - 1 F1-micro instance per month\u003C\/td\u003E\u003C\/tr\u003E\n\u003Ctr\u003E\u003Ctd class='center'\u003E\n\u003Camp-img alt='1 F1-micro Google Virtual Machine, free Linux server, production-ready, optimized for production' layout='responsive' lightbox='' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEg6NAYW6K5q9b3SLFc5TrT5JP2xay4V482_w2NGuMdVr1a0dgz-X-kmyZsGqlw2wgmYk-KqzW1uvUvWKKzKBFwHNV2MHNwbhz_b4vX4ARYA1jJwbFomLsRSKIKtgCm-_wN8CV1R-2ml6494\/s1600\/google-cloud-compute-engine-virtual-machine-VM-00.png' tabindex='0' width='981' height='521'\u003E\u003C\/amp-img\u003E\n\u003Cbr \/\u003E\u003C\/td\u003E\u003C\/tr\u003E\n\u003C\/tbody\u003E\u003C\/table\u003E\n\n\u003Cp\u003EAll we need it's a google account with billing enabled. Indeed, even if the aforementioned F1-micro VM benefit of Google free lines of products (cloud.google.com\/free), we still need billing enabled, to be able to use this free product. Of course, no one says it has to be the salary credit card here, I've managed to enable the billing using a Monese virtual card attached to a Monese bank account. So you can go ahead, open a \u003Ca href='https:\/\/monese.app.link\/AnJTWueUy3?invite=GELUC856' rel='me nofollow noopener' target='_blank'\u003EMonese account\u003C\/a\u003E, top-up some money via bank transfer or credit card if you prefer (needed only for Monese account activation) and you are ready to go. \u003C\/span\u003EAnyway, if you don't have yet the billing activated, you should have at your choice a Google Cloud billing activation method to be used when required.\u003C\/p\u003E\n\n\u003Ch2\u003EThe first F1-micro Google Compute Virtual Machine\u003C\/h2\u003E\n\u003Cp\u003EGo to \u003Ca href='https:\/\/console.cloud.google.com\/' rel='me nofollow noopener' target='_blank'\u003EGoogle Cloud Console\u003C\/a\u003E, use the top-left hamburger navigation menu, and go to Compute Engine-\u0026gt;VM Instances, then hit the 'Create [Instance]' button. And that's simple.\u003C\/p\u003E\n\n\u003Cp\u003EHere we have to stick with a VM configuration that fits into free\/always-free Google Compute Engine offers. We have to choose the correct region, zone, machine type, and other settings accordingly to \u003Ca href='https:\/\/cloud.google.com\/free\/docs\/gcp-free-tier' rel='me nofollow noopener' target='_blank'\u003EGoogle documentation\u003C\/a\u003E \u003C\/p\u003E\n\n\u003Ctable align='center' cellpadding='0' cellspacing='5' class='tr-caption-container clear' width='650'\u003E\u003Ctbody\u003E\n\u003Ctr\u003E\u003Ctd class='tr-caption'\u003ECompute Engine - 1 non-preemptible f1-micro VM instance per month\u003C\/td\u003E\u003C\/tr\u003E\n\u003Ctr\u003E\u003Ctd class='center'\u003E\n\u003Camp-img alt='Google Compute Engine, Free Virtual Machine- 1 non-preemptible f1-micro VM instance per month' layout='responsive' lightbox='' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEgXc4-G2Tdb0irT5Y3fm_PlC3U2ACFwKQOIejv6fPk73nEYwnoU64TcjoBx3MbX7szttGtvClWfL-T9A7xVQguA66rOoR59c0U3PAQ9yKMu5canCmYG99hJG018CnAoVOaeJNZg3iUqnpG9\/s1600\/google-cloud-compute-engine-virtual-machine-VM-01.png' tabindex='0' width='1200' height='717'\u003E\u003C\/amp-img\u003E\n\u003Cbr \/\u003E\u003C\/td\u003E\u003C\/tr\u003E\n\u003C\/tbody\u003E\u003C\/table\u003E\n\n\u003Cp\u003EChose the desired machine name, a region from above, a zone, and set Machine configuration to f1-micro machine type, general purpose N1 series, and the Sandy Bridge CPU platform.\u003C\/p\u003E\n\n\u003Ctable align='center' cellpadding='0' cellspacing='5' class='tr-caption-container clear' width='650'\u003E\u003Ctbody\u003E\n\u003Ctr\u003E\u003Ctd class='tr-caption'\u003EGCE VM configuration, step 1\u003C\/td\u003E\u003C\/tr\u003E\n\u003Ctr\u003E\u003Ctd class='center'\u003E\n\u003Camp-img alt='Free Linux server GCE VM, configuration for Google Compute Engine free offer' layout='responsive' lightbox='' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEiJhTNOAoebst6vuQmpBjWXF5xcnV-G1VuDJ0spXVMoXjCxAg3JyxebsALy9jHOCdyaAgua6lAyL9JrUulxPtiL3bhrDsxDwir4Few3K96932H9wTBeJRP7fZZjVCXG3y9sYwhgJCpHPfUw\/s1600\/google-cloud-compute-engine-virtual-machine-VM-06.png\"' tabindex='0' width='497' height='595'\u003E\u003C\/amp-img\u003E\n\u003Cbr \/\u003E\u003C\/td\u003E\u003C\/tr\u003E\n\u003C\/tbody\u003E\u003C\/table\u003E\n\n\u003Cp\u003E\u003Cstrong\u003EThe boot disk\u003C\/strong\u003E (30GB) is Debian 10, can be chosen by clicking the 'Change [boot disk]' where we select Debian 10 and change the size to 30GB - standard persistent disk.\u003C\/p\u003E\n\n\u003Ctable align='center' cellpadding='0' cellspacing='5' class='tr-caption-container clear' width='650'\u003E\u003Ctbody\u003E\n\u003Ctr\u003E\u003Ctd class='tr-caption'\u003EGCE VM configuration, step 2, the Debian Linux boot disk and the service account\u003C\/td\u003E\u003C\/tr\u003E\n\u003Ctr\u003E\u003Ctd class='center'\u003E\n\u003Camp-img alt='1 F1-micro google VM, your free Linux server, production-ready playground' layout='responsive' lightbox='' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEijHpnVFbPjzR0Ve-Twr49wSkZrtxQSX8tmA-E3jwmY3eGLwcBb-69UMKUR5JCTab5nfD0US6Du5HYKG6Ig211m4_6U8xXSSpq_Mj5UYdSq1JmteNcJf3oI812W2Kof73nyIGdvaiM8evLZ\/s1600\/google-cloud-compute-engine-virtual-machine-VM-07.png\"' tabindex='0' width='482' height='263'\u003E\u003C\/amp-img\u003E\n\u003Cbr \/\u003E\u003C\/td\u003E\u003C\/tr\u003E\n\u003C\/tbody\u003E\u003C\/table\u003E\n\n\u003Cp\u003E\u003Cstrong\u003EThe 'Identity and API access'\u003C\/strong\u003E, Service account, could be set to 'No service account'.\u003C\/p\u003E\n\n\u003Cp\u003E\u003Cstrong\u003EThe firewall\u003C\/strong\u003E: if you are going to use this machine for web-based projects then HTTP\/S traffic should be allowed. \u003C\/p\u003E\n\n\u003Ctable align='center' cellpadding='0' cellspacing='5' class='tr-caption-container clear' width='650'\u003E\u003Ctbody\u003E\n\u003Ctr\u003E\u003Ctd class='tr-caption'\u003EGCE VM configuration, step 3, firewall for HTTP trafic\u003C\/td\u003E\u003C\/tr\u003E\n\u003Ctr\u003E\u003Ctd class='center'\u003E\n\u003Camp-img alt='1 F1-micro google VM, your free Linux server, production-ready playground' layout='responsive' lightbox='' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEi1aeRw0hQtlISuh6rtF6emGqoZbVCs6dJ0IvK8QzLVAJ_GysNDmlHbW791B6ZlubB8jitnY57Z6drus12-TibKkjnmleVQtyyyISweCCiarRa3YgreW1uDweEcIKqgh6Mn0nQTeL9qx98W\/s1600\/google-cloud-compute-engine-virtual-machine-VM-08.png' tabindex='0' width='487' height='84'\u003E\u003C\/amp-img\u003E\n\u003Cbr \/\u003E\u003C\/td\u003E\u003C\/tr\u003E\n\u003C\/tbody\u003E\u003C\/table\u003E\n\n\u003Ch3\u003EThe machine security\u003C\/h3\u003E\n\n\u003Cp\u003EJust check all security-related checkboxes around here and add the public part of an ssh access key (using ssh keys is the most secure method of accessing a headless Linux server).\u003C\/p\u003E\n\n\u003Ctable align='center' cellpadding='0' cellspacing='5' class='tr-caption-container clear' width='650'\u003E\u003Ctbody\u003E\n\u003Ctr\u003E\u003Ctd class='tr-caption'\u003EGCE VM configuration, step 4, general security and ssh keys for terminal access\u003C\/td\u003E\u003C\/tr\u003E\n\u003Ctr\u003E\u003Ctd class='center'\u003E\n\u003Camp-img alt='1 F1-micro google VM, your free Linux server, production-ready playground' layout='responsive' lightbox='' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEgBU-S6a1fueuULBqQRkcHkSSP_NMEEsjP5Gh9SLjYoQUAgQY7fwYft_ntw_8TGq2VdIkiIRfmE-QPJXamTuYd_xul4Wh5dFRylY8AO4zUAud0I02x-CCQezCKa_UHfRluVCXxw3AVTKENz\/s1600\/google-cloud-compute-engine-virtual-machine-VM-09.jpg' tabindex='0' width='498' height='320'\u003E\u003C\/amp-img\u003E\n\u003Cbr \/\u003E\u003C\/td\u003E\u003C\/tr\u003E\n\u003C\/tbody\u003E\u003C\/table\u003E\n\n\u003Cp\u003E\u003Ci class='material-icons'\u003Ehelp_outline\u003C\/i\u003E To generate a secure pair of ssh keys use your terminal (from Ubuntu\/macOS) and run:\u003Cbr \/\u003E\u003Cdiv class=\"code\"\u003E\u003Cpre\u003Essh-keygen -t ecdsa -f ~\/.ssh\/a-ssh-key-name-here -C your-lowercased-name-here\u003C\/pre\u003E\u003C\/div\u003E, choose a strong password, then, after the command completes, display (and select\/copy\/paste the content) the corresponding public part of the just created ssh key\u003Cbr \/\u003E\u003Cdiv class=\"code\"\u003E\u003Cpre\u003Ecat ~\/.ssh\/a-ssh-key-name-here.pub\u003C\/pre\u003E\u003C\/div\u003E\u003C\/p\u003E\n\n\u003Ch3\u003EThe machine disk\/s\u003C\/h3\u003E\n\n\u003Cp\u003EHere we can encrypt the disk by using a custom-supplied key and the code for generating such a disk encryption-key is: \u003Cbr \/\u003E\u003Cdiv class=\"code\"\u003E\u003Cpre\u003Eecho -n '12345678901234567890123456789012' | base64\u003C\/pre\u003E\u003C\/div\u003E where you have to provide a 32 characters fixed length passphrase instead of 123..., space can be included.\u003C\/p\u003E\n\n\u003Ctable align='center' cellpadding='0' cellspacing='5' class='tr-caption-container clear' width='650'\u003E\u003Ctbody\u003E\n\u003Ctr\u003E\u003Ctd class='tr-caption'\u003EGCE VM configuration, step 5, hdd security\u003C\/td\u003E\u003C\/tr\u003E\n\u003Ctr\u003E\u003Ctd class='center'\u003E\n\u003Camp-img alt='1 F1-micro google VM, your free Linux server, production-ready playground' layout='responsive' lightbox='' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhvBRgdXVsekgumRSr1ibZR1L_fpjrXTsigW8jGnAE8uT_4d6ktsUtsKqR3rAqqWRpIMJ94qIL2PUpmZkmYzgySelOBRa8ccq-jpIMX6bE348q3r8glcEKKitDcvKsJlIzEfpnlTpSCtjsg\/s1600\/google-cloud-compute-engine-virtual-machine-VM-10.png' tabindex='0' width='506' height='408'\u003E\u003C\/amp-img\u003E\n\u003Cbr \/\u003E\u003C\/td\u003E\u003C\/tr\u003E\n\u003C\/tbody\u003E\u003C\/table\u003E\n\n\u003Ch3\u003EThe machine networking\u003C\/h3\u003E\n\n\u003Cp\u003ETo reserve a public IP we have to go to Networking and edit the 'default' Network interface. This is a two step action.\u003C\/p\u003E\n\n\u003Ctable align='center' cellpadding='0' cellspacing='5' class='tr-caption-container clear' width='650'\u003E\u003Ctbody\u003E\n\u003Ctr\u003E\u003Ctd class='tr-caption'\u003EGCE VM configuration, step 6, the network and the public IP\u003C\/td\u003E\u003C\/tr\u003E\n\u003Ctr\u003E\u003Ctd class='center'\u003E\n\u003Camp-img alt='1 F1-micro google VM, your free Linux server, production-ready playground' layout='responsive' lightbox='' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhI2i6C9fR-wJGuPVLQIj1PGNkLfXD-E6QpjZw6swHsZ1R_c0VC6U_FkRZaRz9OYWXV4As5J3uw_vglyX0ifnbIvftd5y8GGyCQAGH97cR2fjcYcc_GVAm_HODxVbPt_XIW-r_Ch58CL8Bq\/s1600\/google-cloud-compute-engine-virtual-machine-VM-11.png' tabindex='0' width='503' height='357'\u003E\u003C\/amp-img\u003E\n\u003Cbr \/\u003E\u003C\/td\u003E\u003C\/tr\u003E\n\u003Ctr\u003E\u003Ctd class='center'\u003E\n\u003Camp-img alt='1 F1-micro google VM, your free Linux server, production-ready playground' layout='responsive' lightbox='' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEjpT4qNL1W-qp5IsxfY31ygXmZEmTZGHusjKrC2NMGxPJ33YVcaUUEnLegL-hlASB59F4TRfY9AbsxYFnu9KpqQVGZZqu1RFDLQIqmYfQ9bzStL2vZZulRMEGZhwVCev4UvO2cCqURUDWZ7\/s1600\/google-cloud-compute-engine-virtual-machine-VM-12.png' tabindex='0' width='654' height='456'\u003E\u003C\/amp-img\u003E\n\u003Cbr \/\u003E\u003C\/td\u003E\u003C\/tr\u003E\n\u003C\/tbody\u003E\u003C\/table\u003E\n\n\u003Cp\u003EAnd that's it regarding \u003Cstrong\u003Ethe hardware configuration\u003C\/strong\u003E, take another look to ensure you will not be billed for the configuration, and if everything looks well then we can go ahead, save\/create the machine then ssh connect to it after all (using a terminal emulator). \u003C\/p\u003E\n\n\u003Ctable align='center' cellpadding='0' cellspacing='5' class='tr-caption-container clear' width='650'\u003E\u003Ctbody\u003E\n\u003Ctr\u003E\u003Ctd class='center'\u003E\n\u003Camp-img alt='GCE F1-micro VM, free monthly usage' layout='responsive' lightbox='' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEiJCKk_vj_2Xy1w1ry3xw8N4k3Wa_aLDSyzO4O7eB5TIqB6nQjAVi0SNGnVvjJpX9ckRyMDNTdiOH8EiP3ouNy8dXsWUfvY8phAqCB69CKN_5f40F3qWfasTqlVijx0x3Yz0atzEcDGNj7N\/s1600\/google-cloud-compute-engine-virtual-machine-VM-14.png' tabindex='0' width='484' height='259'\u003E\u003C\/amp-img\u003E\n\u003Cbr \/\u003E\u003C\/td\u003E\u003C\/tr\u003E\n\u003C\/tbody\u003E\u003C\/table\u003E\n\n\u003Ch2\u003EFirst steps inside our Debian Linux headless server\u003C\/span\u003E\u003C\/h2\u003E\n\u003Cp\u003EConnect to the server using the key generated with the ssh-keygen command, and the public IP address of the server:\u003Cbr \/\u003E\u003Cdiv class=\"code\"\u003E\u003Cpre\u003Essh your-lowercased-name-here@12.34.56.789 -i ~\/.ssh\/a-ssh-key-name-here \u003C\/pre\u003E\u003C\/div\u003E\u003C\/p\u003E\n\n\u003Cp\u003EAs the F1-micro VM machine-type is using a small amount of memory, we can free-up some memory (actually ~100MB of RAM) by getting rid of some google injected software and by enabling 2GB of swap memory.\u003C\/p\u003E\n\n\u003Cp\u003EBut first, we have to do some updates and small configs regarding the server configuration.\u003C\/p\u003E\n\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003Esudo apt update\nsudo apt install locales\nexport PATH=$PATH:\/usr\/sbin\n\nsudo dpkg-reconfigure locales\n#look for and mark for installation en_US.UTF-8\n\nsudo nano \/etc\/default\/locale\n# add LANG and LC_ALL sequence to the file\nLANG=en_US.UTF-8\nLC_ALL=en_US.UTF-8\n# then close the file (Ctrl+X, Save (Y), Enter\/Return)\n\nsudo apt install bash-completion\nsudo nano \/etc\/profile\n#add bash_completion (if --- fi sequence) to the end of the file\nif [ -f \/etc\/bash_completion ]; then\n . \/etc\/bash_completion\nfi\n# then close the file (Ctrl+X, Save (Y), Enter\/Return)\n\n# remove google software\nsudo apt remove --purge google-cloud-sdk python-google-compute-engine python3-google-compute-engine google-osconfig-agent google-compute-engine-oslogin google-guest-agent\nsudo apt autoremove\n\n#and upgrade\nsudo apt upgrade\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\u003Cp\u003E\u003Cstrong\u003EThen we can allocate 2GB from HDD space to be used as SWAP memory.\u003C\/strong\u003E\u003C\/p\u003E\n\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003E\nsudo fallocate -l 2g \/mnt\/swap\nsudo chmod 600 \/mnt\/swap\nsudo mkswap \/mnt\/swap\nsudo swapon \/mnt\/swap\necho '\/mnt\/swap swap swap defaults 0 0' | sudo tee -a \/etc\/fstab\n\u003C\/pre\u003E\u003C\/div\u003E\n\n\u003Cp\u003EAnd reboot the machine afterall \u003Ccode\u003Esudo reboot\u003C\/code\u003E\u003C\/p\u003E\n\n\u003Cp\u003EIf after the reboot machine name (hostname) is changed (to debian), the command to change to the desired one is: \u003Ccode\u003Esudo hostnamectl set-hostname your-desired-machine-name-as-declared-in google-console\u003C\/span\u003E\u003C\/code\u003E, and if you want to monitor the server resources usage, the htop \u003Ccode\u003Esudo apt install htop\u003C\/code\u003E will help a lot.\u003C\/p\u003E\n\n\u003Cp\u003EAnd that's it, the server is freed-up of unnecessary resource eatings, has 2GB or swap memory, and is available 24h for the need. \u003C\/p\u003E\n\n\u003Ctable align='center' cellpadding='0' cellspacing='5' class='tr-caption-container clear' width='650'\u003E\u003Ctbody\u003E\n\u003Ctr\u003E\u003Ctd class='tr-caption'\u003EResources used on the barebone Debian 10 Linux server\u003C\/td\u003E\u003C\/tr\u003E\n\u003Ctr\u003E\u003Ctd class='center'\u003E\n\u003Camp-img alt='Accessing the F1-micro free GCE VM within the Gnome terminal and display the used resources' layout='responsive' lightbox='' src='https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEiO9YOc6Dk95fbj8oE_x_BFIEkHlrkYW9bwkN2-ULF03fukvbGAwsFHCqGfdUuNdJ8ZEFDVxE3GBwxpdC3mlPq_VHWsDxV_Fp1ourOarvi8pho70xU569iGP-3Q1m05zjYpLw-rlhZmGcIb\/s1600\/google-cloud-compute-engine-virtual-machine-VM-18.png' tabindex='0' width='768' height='491'\u003E\u003C\/amp-img\u003E\n\u003Cbr \/\u003E\u003C\/td\u003E\u003C\/tr\u003E\n\u003C\/tbody\u003E\u003C\/table\u003E\n\n\u003Cp\u003EEnjoy!\u003C\/p\u003E"},"link":[{"rel":"edit","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/1447364845382184323?v=2"},{"rel":"self","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/1447364845382184323?v=2"},{"rel":"alternate","type":"text/html","href":"https:\/\/www.b247.eu.org\/2020\/07\/1-F1-micro-google-VM-your-Linux-server-production-ready-playground.html","title":"1 F1-micro google VM, your Linux server, production-ready playground"}],"author":[{"name":{"$t":"b247.eu.org"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/05580857583369963260"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"32","height":"32","src":"\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhCOFmryd_Wq7VuVeGmjJ4NXGLdVWFzH6sMG0LOgTugMQaZvlG44DsimJ7u7mhOuWfM5kC1eF9YAieX1YdqiWauCF_Imxv6HXrqaQIiyG5-52rceHYo9fYFhENwp9dKy2g\/s113\/logo600px.png"}}],"media$thumbnail":{"xmlns$media":"http://search.yahoo.com/mrss/","url":"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEis816Vpa1WGc0Uy52tLZ7yJLcJbqP6f_rtsCYMx0qI-zvvZaMPqbp0gJEUZbJTc4XHgdeG6n1muiGa4qINyXYocNl-09osNjTD5RvD2U7kcogC48RVMCbtllWKp5Z5YUtcHzGT3MOwSQEq\/s72-c\/gce-f1-vm.jpg","height":"72","width":"72"}},{"gd$etag":"W/\"DUQCQXozcSp7ImA9XxZVGEQ.\"","id":{"$t":"tag:blogger.com,1999:blog-6816195014656817423.post-2396312984800994904"},"published":{"$t":"2019-11-09T04:48:00.001-08:00"},"updated":{"$t":"2020-12-02T00:22:40.489-08:00"},"app$edited":{"xmlns$app":"http://www.w3.org/2007/app","$t":"2020-12-02T00:22:40.489-08:00"},"category":[{"scheme":"http://www.blogger.com/atom/ns#","term":"PostgreSQL"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Web Development"}],"title":{"$t":"PostgreSQL row-level table audit (back in time) hstore restore"},"content":{"type":"html","$t":"\u003Cnoscript\u003E\n\u003Cimg alt=\"PostgreSQL row-level table audit (back in time) hstore restore\" src=\"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEiJGXz1-EVaJaXUuK_kFjcDH9Nivug6u4ya7GZoUL5AQcTZHq2cQYz6yhAwtJs1HwUZHiOuycHCk60JGZRmEo4841bXbC2EQTvtRsm7F60tUTfdSIjDQKw23dqe3pbFzC0-w4mmipwQy_Q6\/s1600\/postgres-hstore-restore-mika-baumeister-Wpnoqo2plFA-unsplash-856x397-optim.jpg\" data-original-width=\"856\" data-original-height=\"397\" \/\u003E\n\u003C\/noscript\u003E\n\n\u003Cp\u003EKeeping track of database table data changes can be a software development requirement. This approach (auditing a database table) can be used to revert data to a previous state, to restore deleted data or simply to create audit reports.\u003C\/p\u003E\n\n\u003Cp\u003EThe audit approach used by me and described in this article is based on hstore and Postgres triggers. Hstore is a key-value store within Postgres, available through contrib extensions.\u003C\/p\u003E\n\n\u003Cp\u003ETo start using the audit we will need at least a database and a table to monitor for data modifications. I'll use a generic database named 'demo_db' and a generic table named 'demo_users' within.\u003C\/p\u003E\n\n\u003Cp\u003E\u003Cstrong\u003EFirst, we need to enable the hstore extension for the database in use.\u003C\/strong\u003E\nThe hstore extension must be enabled by the \u003Ccode\u003Epostgres\u003C\/code\u003E user by executing the following command.\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003ECREATE EXTENSION hstore;\u003C\/pre\u003E\u003C\/div\u003E\n\n\u003Cp\u003EFrom now one, all SQL commands can be executed as a regular user using either PgAdmin query tools either the \u003Ccode\u003Epsql\u003C\/code\u003E console (server shell or Pgadmin plugin).\u003C\/p\u003E\n\n\u003Cp\u003E\u003Cstrong\u003ESecond, we need to execute the 2ndQuadrant \u003Ccode\u003Eaudit.sql\u003C\/code\u003E file\u003C\/strong\u003E available on GitHub (link available at the bottom of this article) to create the audit schema, tables, and triggers that will be used to monitor and keep modifications history for the monitored tables. The \u003Ccode\u003Eaudit.sql\u003C\/code\u003E content can be executed in the PgAdmin query window or the file can be imported using the \u003Ccode\u003Epsql \\i\u003C\/code\u003E approach (local download needed).\u003C\/p\u003E\n\n\u003Cp\u003EAnd that's it, now we can enable the audit on desired table\/s using the command:\u003C\/p\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003ESELECT audit.audit_table('demo_users');\u003C\/pre\u003E\u003C\/div\u003E\n\n\u003Cp\u003EThe 'demo_users' table used in this example has a basic structure with three columns and a serial one (id) used as the primary key (pk):\u003C\/p\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003ECREATE TABLE demo_users\n(\n  id serial NOT NULL,\n  username character varying NOT NULL,\n  birthdate date NOT NULL,\n  level integer NOT NULL DEFAULT 0,\n  CONSTRAINT demo_users_pkey PRIMARY KEY (id)\n);\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\u003Cp\u003E\u003C\/p\u003E\n\u003Cdiv class=\"code info\"\u003E\u003Ci class=\"material-icons large3em\"\u003Einfo\u003C\/i\u003E When enabling audit on the 'demo_users' table, some triggers will be created on the table and every \u003Ccode\u003EINSERT\/UPDATE\/DELETE\u003C\/code\u003E operation on it will be logged to  'audit.logged_actions' schema table.  The important columns on this 'logged_actions' table are 'row_data' and 'changed_fields', both of type hstore, columns where an exact copy of old and modified row data is stored upon a CRUD operation. The combinations between these two columns and other ones (eg event time, event id, maybe some other custom columns like a session application user,  etc) represent a restore point and data within can be used to restore a production table data to a previous state or to create and display modifications history for that table.\u003C\/div\u003E\n\n\u003Cp\u003ETo check that our audit triggers do its job, let's INSERT\/UPDATE\/DELETE some rows on the audited 'demo_users' table and see how our table modifications get stored and, later, how to work with these restore points.\u003C\/p\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003EINSERT INTO demo_users (username, birthdate, level) VALUES ('demo1','2000-01-01'::date, 0);\nUPDATE demo_users SET birthdate = '2001-02-01'::date, level = 1 WHERE lower(username) = 'demo1';\nUPDATE demo_users SET level = 2 WHERE lower(username) = 'demo1';\nDELETE FROM demo_users WHERE lower(username) = 'demo1';\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\n\u003Cp\u003EAfter all these operations, the 'demo_users' table will be empty and the 'audit.logged_actions' one will host a restore point for each operation executed above.\u003C\/p\u003E\n\n\u003Ch2\u003EA basic report that shows all the restore points for our 'demo_users' Postgres table\u003C\/h2\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003ESELECT event_id, action_tstamp_clk::timestamp(0), action,(populate_record(null::public.demo_users, coalesce(logged_actions.row_data,'')::hstore || coalesce(logged_actions.changed_fields,'')::hstore)).* FROM audit.logged_actions WHERE table_name = 'demo_users';\n\n event_id |  action_tstamp_clk  | action | id | username | birthdate  | level \n----------+---------------------+--------+----+----------+------------+-------\n        1 | 2019-11-03 08:17:18 | I      |  1 | demo1    | 2000-01-01 |     0\n        2 | 2019-11-03 08:17:29 | U      |  1 | demo1    | 2001-02-01 |     1\n        3 | 2019-11-03 08:17:43 | U      |  1 | demo1    | 2001-02-01 |     2\n        4 | 2019-11-03 08:19:34 | D      |  1 | demo1    | 2001-02-01 |     2\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\u003Cp\u003EThis report shows the 'demo_users' modification history in a form of restore points so we can use it to restore our 'public.demo_users' table rows (entirely, by a restore point id or by other advanced filters). The only secret here is the 'populate_record' hstore function, after that it's up to us on what pieces of information we need to add to our report (eg standard columns from 'logged_actions') or how we fine-tune the filters (eg \u003Ccode\u003E ... AND (row_data-\u003E'id')::INT = 1 \u003C\/code\u003E),  if we need a targeted report or specific restore points.\u003C\/p\u003E\n\n\n\u003Ch2\u003ECase study, restore deleted rows using restore points from the audit schema\u003C\/h2\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003EINSERT INTO demo_users SELECT * FROM (\nSELECT\n  (populate_record(null::public.demo_users, logged_actions.row_data)).*\nFROM\n  audit.logged_actions\nWHERE\n table_name = 'demo_users' AND action = 'D' -- AND (row_data-\u003E'id')::INT = 1\n -- event_id = 4\n) q ON CONFLICT DO NOTHING;\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\u003Cp\u003EThis query first generates a restore point report (as a subquery q) and uses the result to insert rows back to our public schema table. The (q) subquery can be fine-tunned with filters just as in the basic report (eg we can restore entire table deleted data, we can restore deleted data just for a specific user or we can restore data to a specific restore point id). The second and third case is already present in the query but commented with '--'.\nIt's up to us what data we need back.\u003C\/p\u003E\n\n\u003Cp\u003EAfter executing the restore query our deleted user\/s is now back to the exact state it was before deletion.\u003C\/p\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003ESELECT * FROM demo_users;\n id | username | birthdate  | level \n----+----------+------------+-------\n  1 | demo1    | 2001-02-01 |     2\n\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\u003Cp\u003EWe have now the data back to the state it was upon deletion, but we can go further and restore our data to any logged restore point, this time by replacing row\/s data with data available in those restore points.\u003C\/p\u003E\n\n\u003Ch2\u003ECase study, restore row\/s data (by updating their content) with data available on the audit schema\u003C\/h2\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003EUPDATE demo_users\nSET  (id, username, birthdate, level) = (q.id, q.username, q.birthdate, q.level)\nFROM   \n(\nSELECT\n  (populate_record(null::public.demo_users, coalesce(logged_actions.row_data,'')::hstore || coalesce(logged_actions.changed_fields,'')::hstore)).*\nFROM\n  audit.logged_actions\nWHERE\n    event_id = 2\n -- table_name = 'demo_users' AND action = 'U' -- AND (row_data-\u003E'id')::INT in (1,2,3,4)\n) q\nWHERE demo_users.id = q.id;\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\u003Cp\u003EAs we can see, our above query uses the same logic as the INSERT one, this time using the UPDATE FROM command. Again, the secret is the (q) subquery that could be extended with filters to match an exact usage case. Just remember my first ProTip to individually execute the q subquery before anything else just to be sure and see (mainly if playing with advanced filters) what rows will be updated upon executing the entire query.\u003C\/p\u003E\n\n\u003Cp\u003EAfter that, we can browse and see that our user\/s data are now in a previous desired state.\u003C\/p\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003ESELECT * FROM demo_users;\n id | username | birthdate  | level \n----+----------+------------+-------\n  1 | demo1    | 2001-02-01 |     1\n\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\n\u003Ch2\u003EPro Tips:\u003C\/h2\u003E\n\u003Cp\u003E\u003Cstrong\u003EProTip 1: Don't forget to use indexes that match your query filters\u003C\/strong\u003E, the hstore has GiST and GIN index support but in our audit schema (and the examples) more important is that hstore keys can be used in btree indexes just as with regular columns. A proper index that will match our example query filters will be:\u003C\/p\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003ECREATE INDEX logged_actions_table_name_row_data_id_idx\n  ON audit.logged_actions\n  USING btree\n  (table_name COLLATE pg_catalog.\"default\", ((row_data -\u003E 'id'::text)::integer));\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\u003Cp\u003E\u003C\/p\u003E\n\u003Cdiv class=\"code info\"\u003E\u003Ci class=\"material-icons large3em\"\u003Einfo\u003C\/i\u003E When we have a small amount of data stored to a database table the index may never be used, but to ensure that we have enabled a usable one, even with a small amount of data we can do that by setting a temporary sequential scan bypass and test our query with \u003Ccode\u003Eexplain analyze\u003C\/code\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003Eset enable_seqscan = 0;\nexplain analyse\nselect ...;\n\u003C\/pre\u003E\n\u003C\/div\u003E\nthat will show us if the index is\/will be used or not (eg \"-\u003E Index Scan using our_index_name on our_table_name ...\" vs \"-\u003E  Seq Scan on our_table ...\")\n \u003C\/div\u003E\n\n\u003Cp\u003E\u003Cstrong\u003EProTip 2: Extend the data stored within our default audit_logged table.\u003C\/strong\u003E\u003Cbr\/\u003E\nIn production, we have application logged users (session users) that operates the database data through the software logic. If we want to store this piece of information (the app logged user id), then it's possible through PostgreSQL configuration parameters.  What we need is one more column called 'app_logged_user' (or anything you like) that defaults to \u003Ccode\u003Ecurrent_setting('APP_LOGGED_USER.id')\u003C\/code\u003E. Here, \u003Ccode\u003EAPP_LOGGED_USER.id\u003C\/code\u003E it's a custom Postgres configuration parameter that can be declared globally inside the postgresql.conf \u003Csup\u003E1)\u003C\/sup\u003E or, better, directly at the database level \u003Csup\u003E2)\u003C\/sup\u003E\u003C\/p\u003E\n\n\u003Csup\u003E1)\u003C\/sup\u003E Edit postgresql.conf (run the psql query \u003Ccode\u003ESHOW config_file;\u003C\/code\u003E to find the exact file location)\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003E#------------------------------------------------------------------------------\n# CUSTOMIZED OPTIONS\n#------------------------------------------------------------------------------\nAPP_LOGGED_USER.id = 0\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\u003Csup\u003E2)\u003C\/sup\u003E run this psql query\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003EALTER DATABASE 'a-database' SET APP_LOGGED_USER.id TO '0'; \n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\u003Cp\u003EWe can use this configuration variable to store the information we need, eg the application logged user id, and we can do that at the application logic by issuing a sql query for that, eg \u003Ccode\u003E $db-\u003EExecute(\"SET 'APP_LOGGED_USER.id' = $1\",array($_SESSION['logged_user_id'])); \u003C\/code\u003E\u003C\/p\u003E\n\n\u003Cp\u003E\u003Cstrong\u003EProTip 3: Store less data to our default audit_logged table.\u003C\/strong\u003E\u003Cbr\/\u003E\nThis is quite easy actually, eg we can use only one timestamp column instead of three and we can get rid of any extra columns that didn't fits in our application logic, eg client_addr\/port\/client_query\/etc.\u003C\/p\u003E\n\n\n\u003Ch2\u003EBonus, a basic report that shows the flow between restore points (old and new values) at a column level\u003C\/h2\u003E\nBy a restore point id\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003ESELECT event_id, action_tstamp_clk, skeys(row_data-(row_data||changed_fields)) as column, svals(row_data-(row_data||changed_fields)) as old_values, svals(changed_fields) as new_values FROM audit.logged_actions WHERE event_id = 11;\n\n event_id |       action_tstamp_clk       |  column   | old_values | new_values \n----------+-------------------------------+-----------+------------+------------\n       11 | 2019-11-03 09:46:55.542603+00 | level     | 0          | 1\n       11 | 2019-11-03 09:46:55.542603+00 | birthdate | 2000-01-01 | 2001-02-01\n(2 rows)\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\u003Cp\u003EBy advanced filtering (table_name and audited table primary key)\u003C\/p\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003ESELECT event_id, action_tstamp_clk, skeys(row_data-(row_data||changed_fields)) as column, svals(row_data-(row_data||changed_fields)) as old_values, svals(changed_fields) as new_values FROM audit.logged_actions WHERE table_name = 'demo_users' AND (row_data-\u003E'id')::int = 1;\n\n event_id |       action_tstamp_clk       |  column   | old_values | new_values \n----------+-------------------------------+-----------+------------+------------\n        2 | 2019-11-03 09:02:51.445477+00 | level     | 0          | 1\n        2 | 2019-11-03 09:02:51.445477+00 | birthdate | 2000-01-01 | 2001-02-01\n        3 | 2019-11-03 09:02:51.445674+00 | level     | 1          | 2\n        7 | 2019-11-03 09:42:53.517047+00 | level     | 2          | 3\n        8 | 2019-11-03 09:43:01.912676+00 | level     | 3          | 2\n        9 | 2019-11-03 09:44:06.185648+00 | level     | 2          | 3\n       13 | 2019-11-03 10:02:04.237171+00 | level     | 3          | 1\n(7 rows)\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\u003Cp\u003E\u003Cstrong\u003EBibliography:\u003C\/strong\u003E\u003Cbr\/\u003E\n\u003Ca href=\"https:\/\/wiki.postgresql.org\/wiki\/Audit_trigger_91plus\" target=\"_blank\" rel=\"nofollow noreferrer noopener\"\u003EAudit trigger 91plus - PostgreSQL wiki\u003C\/a\u003E\u003Cbr\u003E\n\u003Ca href=\"https:\/\/github.com\/2ndQuadrant\/audit-trigger\" target=\"_blank\" rel=\"nofollow noreferrer noopener\"\u003EGitHub - 2ndQuadrant\/audit-trigger: Simple, easily customised trigger-based auditing for PostgreSQL (Postgres)\u003C\/a\u003E\u003Cbr\u003E\n\u003Ca href=\"https:\/\/www.postgresql.org\/docs\/9.6\/hstore.html\" target=\"_blank\" rel=\"nofollow noreferrer noopener\"\u003EPostgreSQL: Documentation: 11: F.16. hstore\u003C\/a\u003E\n\u003C\/p\u003E\n\n\n"},"link":[{"rel":"edit","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/2396312984800994904?v=2"},{"rel":"self","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/2396312984800994904?v=2"},{"rel":"alternate","type":"text/html","href":"https:\/\/www.b247.eu.org\/2019\/11\/postgresql-row-level-table-audit-and-restore-using-hstore.html","title":"PostgreSQL row-level table audit (back in time) hstore restore"}],"author":[{"name":{"$t":"b247.eu.org"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/05580857583369963260"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"32","height":"32","src":"\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhCOFmryd_Wq7VuVeGmjJ4NXGLdVWFzH6sMG0LOgTugMQaZvlG44DsimJ7u7mhOuWfM5kC1eF9YAieX1YdqiWauCF_Imxv6HXrqaQIiyG5-52rceHYo9fYFhENwp9dKy2g\/s113\/logo600px.png"}}],"media$thumbnail":{"xmlns$media":"http://search.yahoo.com/mrss/","url":"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEiJGXz1-EVaJaXUuK_kFjcDH9Nivug6u4ya7GZoUL5AQcTZHq2cQYz6yhAwtJs1HwUZHiOuycHCk60JGZRmEo4841bXbC2EQTvtRsm7F60tUTfdSIjDQKw23dqe3pbFzC0-w4mmipwQy_Q6\/s72-c\/postgres-hstore-restore-mika-baumeister-Wpnoqo2plFA-unsplash-856x397-optim.jpg","height":"72","width":"72"}},{"gd$etag":"W/\"C0QNRnk-cCp7ImA9XBFWGEw.\"","id":{"$t":"tag:blogger.com,1999:blog-6816195014656817423.post-5187863853660190780"},"published":{"$t":"2019-09-24T13:34:00.000-07:00"},"updated":{"$t":"2019-12-08T05:23:17.758-08:00"},"app$edited":{"xmlns$app":"http://www.w3.org/2007/app","$t":"2019-12-08T05:23:17.758-08:00"},"category":[{"scheme":"http://www.blogger.com/atom/ns#","term":"Backup"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Linux"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Ubuntu"}],"title":{"$t":"Restic \u0026 incron\/inotify, continuous backup on folder content change"},"content":{"type":"html","$t":"\u003Cnoscript\u003E\n\u003Cimg alt=\"Restic \u0026 incron\/inotify, continuous backup on folder content change\" src=\"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEgECXM36XN2GKx9060DmT2EFGFIhcm6krTLgn-2Uj0ABxM64HQNUJRD95bfLd6TwuHhj7qzLJh7vjKE59KqcEciEnsLGK230GvH4vhLyZbi46UuvsWh1w9PiijT1Iu6bkK0nmWvDo1TFBgS\/s1600\/restic-backup-856x397-optim.jpg\" data-original-width=\"856\" data-original-height=\"397\" \/\u003E\n\u003C\/noscript\u003E\n\n\u003Cp\u003ERestic is a cross platform “Fast, secure, efficient backup program, Free Software (not just Open Source)”, as the developers says and I'm surely agree with that. It's written in Go and here \u003Ca href=\"https:\/\/github.com\/restic\/others\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"\u003Ehttps:\/\/github.com\/restic\/others\u003C\/a\u003E you can find some of restic key features compared with other backup solutions.\u003C\/p\u003E\n\n\u003Cp\u003EI've recently discovered restic during a live session of the latest Clear Linux OS, when searching through swupd repositories (bundles) for a native backup solution. Clear Linux did't get the chance of a persistent install, the installer not being able to install the OS on my existing EFI partition and a dedicated free space one (dual\/multi boot) alongside my Pop!_OS daily driver. But I've discovered restic and despite the software lacks a GUI, needs some readthedocs research and some terminal skills, I've started to used it, mainly because, at least in my setup, is extremely fast. BTW, I gave up the deja-dup\/duplicity and all the Gnome bells that this combination give me (GUI, Nautilus integration) in favor of restic.\u003C\/p\u003E\n\n\u003Cp\u003EAs always, my logic in doing backups is as follows: store encrypted snapshots (backup) in a local storage (a folder inside internal drive\/external drive) and keep this folder in sync with a reliable cloud storage (I'm using mega.nz here).\u003C\/p\u003E\n\n\u003Cp\u003EFrequency of the backups: here is another point where restic wins against deja-dup\/duplicity, in the past my backup frequency was once a day (using anacron) due to time taken by duplicity to finish the job, now, using restic I can schedule backups only if there is a need of it (a monitored folder\/folders suffer modifications (new\/deleted\/modified files)). Right now I'm doing a full home folder backup at @reboot and every few minutes backups for my Documents folder where I keep most of my work.\u003C\/p\u003E\n\n\u003Cp\u003EDealing with restic is not so easy, but all we need is just some shell copy\/paste commands and some replacement in these commands, e.g. your home folder may be named differently than \/home\/catalin :)\u003C\/p\u003E\n\n\u003Ch2\u003EThe backup software stack\u003C\/h2\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003E\nsudo apt install restic incron\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\u003Ch2\u003EThe restic setup\u003C\/h2\u003E\n\u003Cp\u003ETo be able to automate restic snapshots we need two files, a pseudo config file where to put two environment variables used by restic and another one where to store the password used for encrypting the backup. This files will be stored in the root of our home folder, ~\/\u003C\/p\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003E\nnano .restic.env\n# add bellow two lines to the file, save it and close it\nexport RESTIC_REPOSITORY=\"\/home\/catalin\/BackupSyncR\"\nexport RESTIC_PASSWORD_COMMAND=\"gpg -d .restic.passwd.gpg\"\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\u003Cp\u003E\u003Ccode\u003ERESTIC_REPOSITORY\u003C\/code\u003E is the full local path where the snapshots will be saved, \u003Ccode\u003ERESTIC_PASSWORD_COMMAND\u003C\/code\u003E is a shell command that reads the encryption password from a gpg encrypted file, we will create this file right away.\u003C\/p\u003E\n\n\u003Cdiv class=\"code info\"\u003E\n\u003Ci class=\"material-icons large3em\"\u003Einfo\u003C\/i\u003E If using \u003Ccode\u003ERESTIC_PASSWORD_COMMAND\u003C\/code\u003E seems to be complicated (generate and maintain a copy of a private key) or the target computer is a server, not a desktop computer, then we can use a plain encryption password instead (\u003Ccode\u003Eexport RESTIC_PASSWORD=\"our-plain-password-here\"\u003C\/code\u003E (just make sure to remember this password for worst-case scenarios) and skip the fuss of generating the GnuPG key and the .restic.passwd.gpg encrypted password file)\n\u003C\/div\u003E\n\n\u003Cp\u003E\u003C\/p\u003E\n\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003E\n# generate a GnuPG key\ngpg --full-generate-key\n# select 2 (DSA) -\u003E 2048 as keysize -\u003E 0 = key does not expire -\u003E Real name -\u003E email -\u003E a passphrase to protect the new created key (remember this password\/passphrase)\n\n# create a temporary file to store in it a super strong password (https:\/\/www.grc.com\/passwords.htm is the right place for such a password)\necho \"AjuEoKVsJLNElLmKPydYg3OlLQTd0BZRG5HprOJRZTqz3AofHWUwSHpCNxLn4RR-the password-used-to-encrypt-backup\" \u003E .restic.passwd\n# encrypt the .restic.passwd file then remove the plain version of it\ngpg -e .restic.passwd \u0026\u0026 rm .restic.passwd\n# enter the email used on your previous GnuPG key (eg catalin@pop-os) when prompted\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\u003Cp\u003E\u003C\/p\u003E\n\u003Cdiv class=\"code info\"\u003E\n\u003Ci class=\"material-icons large3em\"\u003Epan_tool\u003C\/i\u003E The private GnuPG key (can be exported from \"Paswords and Keys\") and the .restic.passwd.gpg encrypted file (used to encrypt\/decrypt the backup) must be kept in a copy to a separate storage space (eg external drive\/USB, email, you name it) so in case of complete crash\/SSD failure\/data loss, we will have access to them. If a plain password was used instead then there is nothing to care about, just to remember that plain password.\n\u003C\/div\u003E\n\n\u003Cp\u003ENow we have all we need to start our scheduled backup, but first we need to init the restic repository (declared in the .restic.env file, RESTIC_REPOSITORY)\u003C\/p\u003E\n\n\u003Ch2\u003EInit the restic repository\u003C\/h2\u003E\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003E\n. ~\/.restic.env  ; restic init\n# the password for the key used to encrypt .restic.passwd.gpg will be requested, enter it and check the \"Save in the password manager\"\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\u003Ch2\u003EOptional but highly recommended\u003C\/h2\u003E\n\u003Cp\u003EIf you are using a cloud storage provider, now it's a good time to put in sync the “BackupSyncR” folder with it. I'm using mega.nz — megasync utility for that.\u003C\/p\u003E\n\n\u003Ch2\u003ESchedule backups\u003C\/h2\u003E\n\u003Cp\u003EWe will use cron for that, with a trick :)\u003C\/p\u003E\n\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003E\ncrontab -e\n# add this two lines\n@reboot   sleep 60 ; . ~\/.restic.env \u0026\u0026 restic backup ~\/ --exclude Documents --exclude Downloads --exclude BackupSyncR --exclude .cache --exclude gvfs-metadata --exclude .dbus --exclude *cache* --exclude *cookies* --exclude *places*\n*\/15 * * * *  [ -f ~\/.restic.run ] \u0026\u0026 rm .restic.run \u0026\u0026 . ~\/.restic.env \u0026\u0026  restic backup ~\/Documents\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\u003Cp\u003E\u003C\/p\u003E\n\n\u003Cdiv class=\"code info\"\u003E\n\u003Ci class=\"material-icons large3em\"\u003Einfo\u003C\/i\u003E Monitoring the cron execution is quite simple if we have an MTA installed in our system, and here I recommend the DragonFly MTA (\u003Ccode\u003Esudo apt install dma\u003C\/code\u003E). Then all cron executions will be logged to \u003Ccode\u003E\/var\/mail\/$USER\u003C\/code\u003E and more than these local emails can be accessed from Thunderbird via an \"Add Other account\" - Unix Mailspool (Movemail).\n\u003C\/div\u003E\n\n\u003Cp\u003EFirst line is executed in 60 seconds upon OS login, it loads restic environment variables and execute backup of the  home folder with some excludes, mainly the restic repository itself (yes, in my setup it is located inside home folder), some special folders\/files and the Documents folder which (Documents) is scheduled for snapshots in the next crontab line every 15min but only if needed.\u003Cbr\/\u003E\nThe trick is that the Documents folder gets snapshoted  only if the file .restic.run exists inside home folder and if yes then it gets deleted  (.restic.run) after backup finishes. Got it? Every 15min we check if a file exists, if yes, execute backup, write to log and delete that file.\u003Cbr\/\u003E\nNow, the file .restic.run gets created every time something special happens into documents folder, mainly if there are new, modified or deleted files or folders inside. How we do that? Simply, by using incron and an incrontab entry.\n\u003C\/p\u003E\n\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003E\nsudo nano \/etc\/incron.allow\n# add your username inside (catalin), save it and close it\nincrontab -e\n# add this line\n\/home\/catalin\/Documents\/        IN_MODIFY,IN_CREATE,IN_DELETE,IN_MOVED_TO touch \/home\/catalin\/.restic.run\n#folder to monitor              events to monitor                         action when an event is triggered\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\u003Ch2\u003ERestore files\u003C\/h2\u003E\n\u003Cp\u003EThe easiest way to restore files\/folders from the restic snapshots is to mount the snapshots and use a file browser to search\/copy data.\u003C\/p\u003E\n\n\u003Cdiv class=\"code\"\u003E\n\u003Cpre\u003E\n# if not created, create a \/mnt folder where to mount the restic snapshots\nsudo mkdir \/mnt\/BackupRestoreR \u0026\u0026 sudo chown catalin.catalin \/mnt\/BackupRestoreR\n# and mount the snapshots in this folder\n. ~\/.restic.env ; restic mount \/mnt\/BackupRestoreR\n\u003C\/pre\u003E\n\u003C\/div\u003E\n\n\u003Cp\u003EAnd if you need more info about restoring files or in general about using restic there is readthedocs.io \u003Ca href=\"https:\/\/restic.readthedocs.io\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"\u003Ehttps:\/\/restic.readthedocs.io\/\u003C\/a\u003E\u003C\/p\u003E"},"link":[{"rel":"edit","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/5187863853660190780?v=2"},{"rel":"self","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/6816195014656817423\/posts\/default\/5187863853660190780?v=2"},{"rel":"alternate","type":"text/html","href":"https:\/\/www.b247.eu.org\/2019\/09\/restic-incron-inotify-continuous-backup-folder-change.html","title":"Restic \u0026 incron\/inotify, continuous backup on folder content change"}],"author":[{"name":{"$t":"b247.eu.org"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/05580857583369963260"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"32","height":"32","src":"\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhCOFmryd_Wq7VuVeGmjJ4NXGLdVWFzH6sMG0LOgTugMQaZvlG44DsimJ7u7mhOuWfM5kC1eF9YAieX1YdqiWauCF_Imxv6HXrqaQIiyG5-52rceHYo9fYFhENwp9dKy2g\/s113\/logo600px.png"}}],"media$thumbnail":{"xmlns$media":"http://search.yahoo.com/mrss/","url":"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEgECXM36XN2GKx9060DmT2EFGFIhcm6krTLgn-2Uj0ABxM64HQNUJRD95bfLd6TwuHhj7qzLJh7vjKE59KqcEciEnsLGK230GvH4vhLyZbi46UuvsWh1w9PiijT1Iu6bkK0nmWvDo1TFBgS\/s72-c\/restic-backup-856x397-optim.jpg","height":"72","width":"72"}}]}}