OERu Technology Blog http://tech.oeru.org/ en Introducing the OERu Tech Blog http://tech.oeru.org/intro <span class="field field--name-title field--type-string field--label-hidden">Introducing the OERu Tech Blog</span> <div class="field field-node--field-blog-tags field-name-field-blog-tags field-type-entity-reference field-label-above"> <h3 class="field__label">Blog tags</h3> <div class="field__items"> <div class="field__item field__item--free--open-source"> <span class="field__item-wrapper"><a href="/taxonomy/term/6" hreflang="en">free &amp; open source</a></span> </div> <div class="field__item field__item--technology"> <span class="field__item-wrapper"><a href="/taxonomy/term/7" hreflang="en">technology</a></span> </div> <div class="field__item field__item--kanban"> <span class="field__item-wrapper"><a href="/taxonomy/term/8" hreflang="en">kanban</a></span> </div> <div class="field__item field__item--devops"> <span class="field__item-wrapper"><a href="/taxonomy/term/9" hreflang="en">devops</a></span> </div> <div class="field__item field__item--foss"> <span class="field__item-wrapper"><a href="/taxonomy/term/10" hreflang="en">foss</a></span> </div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">dave</span></span> <span class="field field--name-created field--type-created field--label-hidden">Thu 08/09/2016 - 13:00</span> <div class="clearfix text-formatted field field-node--body field-name-body field-type-text-with-summary field-label-hidden"> <div class="field__items"> <div class="field__item"><p>The <a href="http://oeru.org">Open Education Resource universitas</a> (OERu) is an open organisation from top to bottom. Our entire technological infrastructure (with a <a href="/node/1" target="_blank">couple exceptions</a>) is built with and on Free and Open Source Software to which I normally refer as "FOSS".</p> <p>We are committed to using FOSS for our infrastructure because:</p> <ul><li>it is consistent with our "open" philosophy for education - if our educational materials and all our OERu planning processes are "open on principle", then delivering them on "closed" (or, more precisely, "proprietary") technologies would be a clanging bit of <a href="https://en.wikipedia.org/wiki/Cognitive_dissonance">cognitive dissonance</a>. There's even a word for it: "prefigurative" - use means that are consistent with your desired end.</li> <li>it allows us to use "best-of-breed" technologies (in the web-space, most innovation happens as FOSS first), combining many single-purpose solutions together in a modular way - via the open standards, open data formats, and open APIs they all support - to form a robust, feature-rich and secure communications and collaboration infrastructure. If a better solution to one of our requirements emerges, we can rapidly adapt our systems to adopt it, ensuring we are responsive and offer a fresh, state-of-the-art, well-supported and technically excellent platform.</li> <li>our partner institutions collaborators become familiar with these tools, and if they find any of them valuable, they can champion their adoption by their home institutions, helped by the instructions provided by this blog or our FOSS code repositories. The money saved and flexibility gained by our partner institutions by doing so are likely to be of substantially greater value than their annual OERu membership fees!</li> <li>Perhaps most importantly, none of our collaborators or learners are <em>ever</em> compelled to accept dubious (and usually exploitative) proprietary software Terms and Conditions to be part of our community. We respect our community's right to privacy and personal data sovereignty from the ground up.</li> </ul><h2>Physical Infrastructure</h2> <p>Our infrastructure is currently hosted on FOSS <a href="https://ubuntu.com">Ubuntu</a> and <a href="https://debian.org">Debian</a> Linux systems provides by four IaaS (Infrastructure as a Service) providers:</p> <ol><li>our <a href="https://www.mediawiki.org">MediaWiki</a> infrastructure, the star of which is <a href="https://wikieducator.org">Wikieducator.org</a>, is hosted on Amazon's "Elastic Compute" EC technology (located in the US, in our case), running Ubuntu Linux 14.04.</li> <li>the rest of our self-hosted infrastructure, hosted on a high-specification virtual machine, "Hetzner", also running Ubuntu Linux 14.04 (based in Germany). More on the many services provided by Hetzner below.</li> <li>more recently, we have created hosting for some of our new services on <a href="http://azure.com" title="Microsoft's Infrastructure-as-a-service Platform.">Azure</a>, taking advantage of an annual grant given to qualifying non-profit organisations by Microsoft. We run Ubuntu Linux 16.04 hosts there. We ensure we do not make use of any proprietary Azure capabilities in any of our automated scripts so that we can shift hosting providers with minimal cost and inconvenience if/when Microsoft changes their free-hosting policy.</li> <li>in 2018, we also received sponsored hosting capacity from NZ's own cloud service provider - whose cloud offering is entirely open source software built on open stack - <a href="https://catalystcloud.nz">Catalyst Cloud</a>. We host our <a href="https://git.oeru.org">code repositories</a> on their infrastructure. Many thanks to Catalyst!</li> </ol><p>Incidentally, the FOSS operating system, Linux, <a href="https://w3techs.com/technologies/details/os-unix/all/all">is the most widely used hosting platform on the Internet </a>today, and <a href="https://w3techs.com/blog/entry/ubuntu_became_the_most_popular_linux_distribution_for_web_servers">Ubuntu is the most widely used</a> "distribution" of Linux.</p> <h2>Externally hosted</h2> <p>We also make use of some externally hosted commercial FOSS services (we pay them for their services) to provide all the functionality we require:</p> <ul><li><a href="http://onlinegroups.net">OnlineGroups.Net</a> for <a href="http://groups.oeru.org">our family of mailing lists</a>.</li> <li><a href="https://mautic.net">Mautic</a> for our newsletter and user-engagement needs (Update 2017-06-16: due to substantial price increases for the hosted Mautic service, we are moving to a self-hosted version <a href="/installing-mautic-php7-fpm-docker-nginx-and-mariadb-ubuntu-1604">set up like this</a>).</li> <li>Update 2017-06-16: we have adopted a new open source Kanban planning tool, <a href="http://kanboard.net">Kanboard</a>, and we're supporting the developer by paying for the hosted service.</li> </ul><h2>Web Applications</h2> <p>We host and maintain a number of websites "that do stuff", otherwise known as web applications. These include:</p> <ul><li>Our <a href="https://course.oeru.org">Course website</a> which acts as a platform for per-course and per-cohort course websites, generated automatically via an OERu innovation: our <a href="/oeru-mediawiki-wordpress-snapshot-toolchain">course "snapshot" process</a> from learning materials formulated on Wikieducator that are transformed into fully-formed, partner-institution-branded websites. Built on the <a href="https://wordpress.org">WordPress</a> blog platform, running in "multisite" mode.</li> <li>Our main <a href="https://oeru.org">OERu Website</a> - which provides information about the organisation relevant to both learners and partners. It is built on the <a href="https://silverstripe.org">Silverstripe</a> Content Management System (CMS).</li> <li>This Technology Blog... which is built on the <a href="https://drupal.org">Drupal</a> CMS (version 8).</li> </ul><h2>Web Services</h2> <p>To maintain control and flexibility, we self-host a myriad of useful web-based resources and services. These include:</p> <ul><li>our <a href="https://cloud.oeru.org">data sharing/digital artefact-storage site</a>, comparable to having our own "Dropbox", is <a href="https://nextcloud.org">NextCloud</a>.</li> <li>our <a href="https://etherpad.oerfoundation.org">collaborative document editing</a> platform is <a href="https://github.com/ether/etherpad-lite">Etherpad-Lite</a>.</li> <li>our two "next generation" online forums, <a href="https://community.oeru.org">Community</a> (for educators and OERu collaborators) and <a href="https://forums.oeru.org">Forums</a> (for learners), built on <a href="http://www.discourse.org/">Discourse</a>.</li> <li>our <a href="https://chat.oeru.org">chat system</a>, a <a href="https://rocket.chat">Rocket.Chat</a> instance, similar to the proprietary Slack platform, replaces our venerable geeks-only platform, IRC (Internet Relay Chat).</li> <li>our <a href="https://plan.oeru.org">planning system</a>, an instance of the <a href="http://wekan.org">Wekan</a> "virtual <a href="https://en.wikipedia.org/wiki/Kanban_board">Kanban board</a>" (Update 2017-06-16: although we still use this a bit, we have found we prefer <a href="http://kanboard.net">Kanboard</a>)</li> <li>our <a href="https://oer.nz/admin">link shortening</a> service is an instance of <a href="http://yourls.org/">YourLS</a>.</li> <li>our <a href="https://mantis.oeru.org">issue tracking</a> service is an instance of <a href="https://www.mantisbt.org">Mantis Bug Tracker</a> (Update 2017-06-16: we've retired this as it wasn't quite the right fit for our users)</li> <li>our integrated <a href="https://links.oeru.org">link sharing "course resource bank"</a> is <a href="https://semanticscuttle.sourceforge.net">Semantic Scuttle</a>.</li> <li>our <a href="https://stats.oeru.org">website usage tracking</a> system built with <a href="http://piwik.org">Piwik</a>.</li> <li>Update 2017-06-16: we have recently set up a <a href="https://github.com/tootsuite/mastodon" title="The Mastodon Federated Social Network">Mastodon</a> instance to facilitate training our learners in the use of social networking without having to resort to a proprietary freedom-compromised platform. Here's <a href="/installing-mastodon-docker-compose-ubuntu-1604">how we did it</a>.</li> <li>Update 2017-06-16: we have a <a href="https://nextcloud.com" title="Open source, self-hosted web-based multi-tenented file store similar to Dropbox, but with more freedom.">NextCloud</a> instance, linked to a <a href="https://www.collaboraoffice.com/">Collabora Online office suite</a> instance, a concurrent editing application similar to Google Docs/Sheets. Howto coming soon!</li> <li>Update 2017-09-22: we have a <a href="https://limesurvey.org" title="Comprehensive open source online survey and polling tool.">Lime Survey</a> instance, replacing our use of Google Forms for conducting web-based surveys and polls.</li> </ul><h2>Code Repositories</h2> <p>We have a convention of documenting (including instructions, source code, and configuration examples) all of our individual implementations FOSS implementations. We have multiple projects for public reference stored at both Bitbucket (we have repositories in both the <a href="https://bitbucket.org/oerf/">Wikieducator</a> and the <a href="https://bitbucket.org/oerf/">OER Foundation</a> projects - Bitbucket is a source code "<a href="https://en.wikipedia.org/wiki/Forge_(software)">forge</a>" run by Atlassian in Australia) and<a href="github.com/oeru"> </a><a href="https://bitbucket.org/wikieducator/">Github</a><a href="github.com/oeru"> </a>(a forge run by Github in the US).</p> <p>Over time, a few of us will be writing up some blog posts on specific technologies to introduce them in a gentle way to those for whom terms like "git" and "pull request" do not yet have a respectable technology-related connotation.</p> <h2>Our Toolbox</h2> <p>To build and maintain our infrastructure, we use a cornucopia of additional FOSS tools. Among these are text editors, monitoring systems, backup tools, debugging environments, "<a href="https://en.wikipedia.org/wiki/DevOps">devops</a>" platforms, container technologies, and many others. We'll no doubt cover some of these in future blog posts.</p> <p>A couple noteworthy tools that all of our institutional partners should know about include:</p> <ul><li>we get all of our SSL (Secure Sockets Layer) certificates, that help keep our users' information secure and private by providing browser-to-server end-to-end encryption from <a href="https://letsencrypt.org/">Let's Encrypt</a>, at no cost. We encourage everyone else to do likewise! Here's our <a href="/protecting-your-users-lets-encrypt-ssl-certs">Let's Encrypt howto</a>.</li> <li><a href="https://en.wikipedia.org/wiki/OpenSSH">OpenSSH</a> (Secure SHell) - which ships with all Linux systems of which we're aware - it's the way we access all of our remote systems securely from anywhere.</li> </ul></div> </div> </div> <section class="field field-node--field-blog-comments field-name-field-blog-comments field-type-comment field-label-above comment-wrapper"> <a name="comments"></a> <div class="comment-form-wrapper"> <h2 class="comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=2&amp;2=field_blog_comments&amp;3=comment" token="CdJY_SzH-b9OZT5uqHtipOPQseJjEJj5aRpmwEa4k8M"></drupal-render-placeholder> </div> </section> Thu, 08 Sep 2016 01:00:54 +0000 dave 2 at http://tech.oeru.org http://tech.oeru.org/intro#comments 2020 OERu Open Technology Overview http://tech.oeru.org/2020-oeru-open-technology-overview <span class="field field--name-title field--type-string field--label-hidden">2020 OERu Open Technology Overview</span> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">dave</span></span> <span class="field field--name-created field--type-created field--label-hidden">Wed 29/07/2020 - 14:56</span> <div class="clearfix text-formatted field field-node--body field-name-body field-type-text-with-summary field-label-hidden"> <div class="field__items"> <div class="field__item"><p>As a matter of principle, the OER Foundation makes use of Free and Open Source Software (FOSS) wherever possible. Partnering with the Foundation provides a distinctive perspective to learn from an organisation with hands-on experience in hosting all its technology infrastructure using FOSS.</p> <p>The foundation has implemented a component-based approach selecting “best-of-breed” open technologies for assembling the OERu’s Digital Learning Environment (DLE) rather than providing learning materials through a single application like a Learning Management System (LMS).</p> <p>There are myriad reasons for taking this approach.</p> <p>FOSS tools are highly advanced and have a low fixed cost as well as <em>negligible variable costs</em> for provision. FOSS tools offer superb functionality and are subject to neither per-server or “per-seat” license fees when self-hosted. If, for convenience, you choose to buy a hosted service featuring a FOSS tool, you always have the option of moving to a self-hosted instance if the service provider’s pricing or terms of use become oppressive.</p> <p>Hosting our own FOSS tools provides the massive advantage - albeit one largely ignored by software users in general - that the Foundation can determine our own Terms of Use, designed to support and enable our learners, rather than impose upon their privacy or legal rights by forcing onerous terms on them as most proprietary hosted services do. This is contrary to most educational software use, where learners are, in effect, forced to accept the terms of use imposed by corporate vendors.</p> <p>With FOSS tools, our learners <em>never have to buy third party software</em>. This means that there are no external cost-barriers to their participation.</p> <p>FOSS tools can be glued together in ways that appear seamless for end users with open standards-based integration approaches, or, in the worst case, small strategic custom development projects. We can achieve incredible integrations using this approach, including our <a href="/wikieducator-notes-oerus-course-feed-aggregation-and-messaging-system">WEnotes</a> system, which discovers course-related posts created by learners in a variety of online tools and services, and <em>brings the Internet to them</em> - it aggregates them for learners to see in the context of each course via its “course feed” like <a href="https://course.oeru.org/lida101/interactions/course-feed/">this one</a> for our Learning in a Digital Age 101 course, aka LiDA101.</p> <p>FOSS tools are managed by communities, often global, who take a shared interest in their projects’ survival, sustainability, and perpetual improvement. They are seldom motivated by profit. Proprietary software tools can be affected by their sole vendor going out of business, being acquired (with the tool merged with other tools or shut down), or the vendor can alter their terms of use at their whimsy.</p> <p>In stark contrast, FOSS tools continue to exist as long as they provide value to the community that sustains them. If a tool does end up going dormant, the community of those dependent on the tool typically share the responsibility of building a migration path to a successor technology rather than it being the sole responsibility of one vendor or forcing users to fend for themselves.</p> <p>Because of its low fixed and variable costs and the distributed development and maintenance responsibilities typifying FOSS projects, we believe <em>our approach results in a sustainable and equitable Digital Learning Environment</em>.</p> <p>A loosely-coupled tools approach allows us to adopt “best of breed” FOSS tools in each software domain and focus our energies on connecting them all in a sensible way that empowers our learners to participate. We can trial and swap emerging standout candidates in and out of our software stack as desired, with no licensing or contractual implications and minimal impact on our learners and collaborators. To keep our staff time costs and risks low, we have adopted common patterns for deploying and managing our tool collection, including the widespread use of Docker server virtualisation and the unparalleled Linux server hosting ecosystem and associated application stacks - almost all modern web-based software is built on one or another FOSS stack (like those centred around PHP, Python, Ruby on Rails, NodeJS, and Java) - you can <a href="/2018-update-oeru-technology-stack">learn more about the specific technologies</a> in our "tech stack". This offers us a tremendous diversity of FOSS applications and allows us to remain current with the constantly changing and often fickle expectations of Internet users. Moreover, we completely avoid the restriction of needing to find tools integrated with a monolithic LMS (in the case of FOSS LMSs like Moodle or Canvas) or somehow anointed or bundled by the LMS’ authors (in the case of proprietary LMSs like Blackboard). Those integrated tools have limited applicability, namely only working properly in the context of a subset of LMSs, and they are therefore seldom as advanced or long-lived as their stand-alone point solution FOSS alternatives.</p> <p>Last, by adopting an array of loosely-coupled FOSS tools, our partners (and non-partners alike) can pick and choose to replicate one or more of the FOSS technologies we use and integrate them into their own technology mixes. We actively lower the barriers to those institutions and individuals benefiting from that FOSS <a href="/">by providing overviews and how-tos</a>.</p> <p>Thanks to our <a href="/working-home">use of open technologies</a> within the Foundation and in collaboration with our Partners, as well as our many years experience as a “distributed organisation”, we were able to <a href="https://oer4covid.oeru.org/resources/community-tools/">respond immediately and decisively to the COVID 19 pandemic</a>, providing crucial technologies, like real-time chat and large scale video conferencing, to our partners and the global community of educators.</p> <p>With all these clear advantages, we are frankly astounded that the rest of the education sector has not adopted the same FOSS approach. We're keen to help those of you out there who recognise this opportunity to put it into practice!</p> </div> </div> </div> <section class="field field-node--field-blog-comments field-name-field-blog-comments field-type-comment field-label-above comment-wrapper"> <a name="comments"></a> <div class="comment-form-wrapper"> <h2 class="comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=33&amp;2=field_blog_comments&amp;3=comment" token="ImPsGPbN_ZXzsN8aUD8FSS9-rzWeJ3G1Q_PxUVa7dcY"></drupal-render-placeholder> </div> </section> Wed, 29 Jul 2020 02:56:50 +0000 dave 33 at http://tech.oeru.org http://tech.oeru.org/2020-oeru-open-technology-overview#comments Working from home http://tech.oeru.org/working-home <span class="field field--name-title field--type-string field--label-hidden">Working from home</span> <div class="field field-node--field-blog-tags field-name-field-blog-tags field-type-entity-reference field-label-above"> <h3 class="field__label">Blog tags</h3> <div class="field__items"> <div class="field__item field__item--covid-19"> <span class="field__item-wrapper"><a href="/taxonomy/term/73" hreflang="en">covid-19</a></span> </div> <div class="field__item field__item--working-from-home"> <span class="field__item-wrapper"><a href="/taxonomy/term/74" hreflang="en">working from home</a></span> </div> <div class="field__item field__item--foss"> <span class="field__item-wrapper"><a href="/taxonomy/term/10" hreflang="en">foss</a></span> </div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">dave</span></span> <span class="field field--name-created field--type-created field--label-hidden">Wed 18/03/2020 - 09:11</span> <div class="clearfix text-formatted field field-node--body field-name-body field-type-text-with-summary field-label-hidden"> <div class="field__items"> <div class="field__item"><p>In light of the current global pandemic, there's lots of interest out there on "working remotely" or "working from home". We've been doing a for years, and it has a lot of advantages... here's how we do it....</p> <p>The OER Foundation (we're the charitable foundation that coordinates the <a href="https://oeru.org">OERu</a>) is a "remote organisation". There is no central office - after all only two of us work for the Foundation full time. We both work from our homes, in <a href="https://osm.org/go/r9ZYCbT?layers=N">Mosgiel</a> and <a href="https://osm.org/go/uoyJ~MZ-?layers=N">Christchurch</a>, two centres about 5 hours drive apart in the South Island of New Zealand. We also have colleagues, who dedicate part of their time to our projects, based at <a href="https://op.ac.nz">Otago Polytechnic</a> in Dunedin. Despite our small (but plucky) team, we collaborate with educators at<em> <a href="https://oeru.org/oeru-partners">40+ tertiary institution partners</a> (universities and polytechnics) in 5 continents around the world who amplify our efforts</em>...  Luckily for us, we both have the benefit of high speed internet access and a knowledge of Free and Open Source Software (FOSS) and how to make it work for our requirements - and scale for a <em>global</em> audience of educators and learners!</p> <h2>How we work</h2> <p>At the OER Foundation, we're task oriented (not time oriented - no timesheets). Every week, I have a set of standard tasks I undertake, as do my colleagues, and we have to get them done. For example, I "go around the (virtual) traps" at the start of each week to ensure all our remote systems are up-to-date and secure as possible. When those are done, we have a bunch of longer term (important-but-not-urgent) projects that are under way, scheduled in a kanban board (we use either our NextCloud's "Desk" plug-in or the very fine <a href="https;//kanboard.net">Kanboard</a> FOSS app) or by agreement.</p> <p>During the day we have frequent communication and collaboration among our small team, as well as with the broader group of OERu partner representatives - our Rocket.Chat system is our virtual meeting room and water cooler for both meeting and informal discussion.</p> <p>A few of us meet online for a video chat each day to catch up on the days work, talk strategy, and socialise a bit. We usually have our chats using Jitsi Meet, which is triggered by Rocket.Chat. Lately, we've been using Big Blue Button a lot more for our catchups, just to mix it up a little - they're both great for small groups (Big Blue Button also supports much larger groups).</p> <p>Here's a table laying out our "work from home" stack:</p> <table style="width: 90%; padding: 4px 8px; border: 0px black solid" summary="The FOSS tools we use to work remotely, all the time."><tbody><tr><th>Technology category</th> <th>Specific FOSS technology</th> <th>Replaces</th> </tr><tr><th>Desktop/Laptop Computers</th> <td>Lenovo Thinkpads</td> <td> </td> </tr><tr><th>Operating System</th> <td><a href="https://ubuntu.com/download/desktop/thank-you?version=18.04.4&amp;architecture=amd64">Ubuntu Linux</a> or <a href="https://linuxmint.com/download.php">Linux Mint</a></td> <td>Microsoft Windows, Apple MacOS</td> </tr><tr><th>Email service</th> <td><a href="https://mailcow.email/">Mailcow</a> (self-hosted - <a href="https://github.com/mailcow/mailcow-dockerized">how to do it</a>)(<a href="https://www.servercow.de/mailcow?lang=en#managed">commercial service</a>)</td> <td>Google Mail, Microsoft Outlook, Fastmail</td> </tr><tr><th>Email client (reading/composing)</th> <td><a href="https://www.thunderbird.net/en-US/">Thunderbird</a> (desktop), <a href="https://sogo.nu/about.html">SOGo</a> (webmail - built into Mailcow)</td> <td>Microsoft Outlook, Apple MailApp, Gmail, Hotmail, etc.</td> </tr><tr><th>Web browser</th> <td><a href="https://www.chromium.org/Home">Chromium</a> or <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> or <a href="https://brave.com/">Brave</a></td> <td>Google Chrome, Microsoft IE or Edge, Apple Safari</td> </tr><tr><th>Chat service, video conf</th> <td><a href="https://rocket.chat">Rocket.Chat</a> (self-hosted - <a href="/upgrading-rocketchat-10x-and-mongodb-40">how to do it</a>)(<a href="https://rocket.chat/pricing#cloud">commercial service</a>) - (access via browser, or <a href="https://rocket.chat/install">desktop and mobile</a> interfaces)</td> <td>Slack, Discord, Gitter, Microsoft Teams</td> </tr><tr><th>Document management and sharing</th> <td><a href="https://nextcloud.com">NextCloud Hub</a> (self-hosted - <a href="/installing-nextcloud-hub-onlyoffice-ubuntu-1804">how to do it</a>)(<a href="https://nextcloud.com/pricing/">commercial service</a> - gov't and education discounts available)</td> <td>Dropbox, Google Drive, Microsoft OneDrive</td> </tr><tr><th>Collaborative documents</th> <td>NextCloud's <a href="https://onlyoffice.com">OnlyOffice</a> integration (self-hosted - <a href="/installing-nextcloud-hub-onlyoffice-ubuntu-1804">how to do it</a>)(<a href="https://nextcloud.com/pricing/">commercial service</a> - gov't and education discounts available)</td> <td>Google Docs, MS Office 365</td> </tr><tr><th>Kanban project management</th> <td>NextCloud's <a href="https://apps.nextcloud.com/apps/deck">Deck app</a> or <a href="https://kanboard.org">Kanboard</a> (commercial service)</td> <td> <p> </p> <p>Trello, MS Project</p> </td> </tr><tr><th>Shared calendaring and contacts</th> <td>NextCloud's <a href="https://apps.nextcloud.com/apps/calendar">Calendaring</a> and <a href="https://apps.nextcloud.com/apps/contacts">Contact</a> apps</td> <td>MS Outlook, iCal, Google Calendars</td> </tr><tr><th>Scheduling (timezone aware)</th> <td>NextCloud's Poll app (part of NextCloud)</td> <td>Doodle Polls</td> </tr><tr><th>Password manager (share and personal)</th> <td><a href="https://bitwarden.com/">BitWarden</a> (self-hosted - <a href="/setting-your-own-bitwarden-password-keeper-and-sync-server">how to do it</a>)</td> <td>LastPass, 1Password, Keepassx, etc.</td> </tr><tr><th>Video Conferencing (&lt; 8 participants)</th> <td><a href="https://meet.jit.si">Jitsi Meet</a> (externally hosted) browser-based or mobile apps (all FOSS)</td> <td>Google Hangouts, Apple Facetime, Microsoft Skype</td> </tr><tr><th>Video Conferencing (webinars, hundreds of participants, breakout rooms, etc.)</th> <td><a href="https://bigbluebutton.org">Big Blue Button</a> (self-hosted)(commercial service <a href="https://moodlecloud.com/app/en/">one</a>, <a href="https://blindsidenetworks.com/services/">two</a>) browser-based, mobile apps available - if you want to try it yourself (gratis) go to the NZOSS' "<a href="https://nzoss.nz/workfromhome">Work from home</a>" page.</td> <td>Zoom</td> </tr><tr><th>Social Media</th> <td><a href="https://joinmastodon.org">Mastodon</a> (self-hosted)</td> <td>Twitter</td> </tr></tbody></table><p>Outside of that stack of software applications, we use lots of other FOSS apps on the desktop, like <a href="https://inkscape.org">Inkscape</a> (for vector drawings, replacing Adobe Illustrator), <a href="https://krita.org">Krita</a> and <a href="https://www.gimp.org">Gimp</a> (to modify images, replacing Adobe Photoshop), <a href="https://kdenlive.org">Kdenlive</a> (to edit video), <a href="https://libreoffice.org">LibreOffice</a> (to edit PDFs, documents, spreadsheets, drawings, presentations, replacing MS Office), <a href="https://www.audacityteam.org/">Audacity</a> (editing audio like voice-overs and podcasts), and many others, and many dozens of development tools to produce digital artefacts. If you're looking for a FOSS tool to do a job, <a href="/contact">let us know</a> - perhaps we can help you find one that's suitable!</p> <p>If you want to learn more about our cloud infrastructure - which involves a whole additional constellation of FOSS technologies - here's a <a href="/2018-update-oeru-technology-stack">review of our 2018 technology stack</a>, which is still current, with which we are providing a full online university...</p> </div> </div> </div> <section class="field field-node--field-blog-comments field-name-field-blog-comments field-type-comment field-label-above comment-wrapper"> <a name="comments"></a> <div class="comment-form-wrapper"> <h2 class="comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=31&amp;2=field_blog_comments&amp;3=comment" token="sLz-SwBuWdXbxVlQ0hryx85rnXi263GuAbWsaTp-zmk"></drupal-render-placeholder> </div> </section> Tue, 17 Mar 2020 20:11:13 +0000 dave 31 at http://tech.oeru.org http://tech.oeru.org/working-home#comments Automatic versioned backups of Sqlite in a Docker Compose container http://tech.oeru.org/automatic-versioned-backups-sqlite-docker-compose-container <span class="field field--name-title field--type-string field--label-hidden">Automatic versioned backups of Sqlite in a Docker Compose container</span> <div class="field field-node--field-blog-tags field-name-field-blog-tags field-type-entity-reference field-label-above"> <h3 class="field__label">Blog tags</h3> <div class="field__items"> <div class="field__item field__item--sqlite"> <span class="field__item-wrapper"><a href="/taxonomy/term/72" hreflang="en">sqlite</a></span> </div> <div class="field__item field__item--bash"> <span class="field__item-wrapper"><a href="/taxonomy/term/58" hreflang="en">bash</a></span> </div> <div class="field__item field__item--backup"> <span class="field__item-wrapper"><a href="/taxonomy/term/57" hreflang="en">backup</a></span> </div> <div class="field__item field__item--ubuntu-linux"> <span class="field__item-wrapper"><a href="/taxonomy/term/12" hreflang="en">ubuntu linux</a></span> </div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">dave</span></span> <span class="field field--name-created field--type-created field--label-hidden">Tue 04/02/2020 - 12:17</span> <div class="clearfix text-formatted field field-node--body field-name-body field-type-text-with-summary field-label-hidden"> <div class="field__items"> <div class="field__item"><p>For relatively lightweight applications that are either in development, or single user, or have limited requirements for concurrency and massive data sets, <a href="https://en.wikipedia.org/wiki/SQLite">SQLite</a> is a superb, full-function, but compact, <em>almost ubiquitous</em> database (it's used on every mobile device, for example).</p> <p>What's more, it's also Free and Open Source Software (FOSS) - its code has been dedicated to the public domain - making it an ideal tool for a principled open organisation like the OER Foundation to incorporate in its fully Free and Open Source Software stack.</p> <p>We use SQLite for quite a few things here at the OER Foundation. For example, this implementation of the backup script was developed for the very impressive fully FOSS version of the <a href="/setting-your-own-bitwarden-password-keeper-and-sync-server">password manager BitWarden </a>we use to manage our secrets at the OER Foundation. Because the data it holds, although very compact, is very precious, we want to back it up right, and ensure we can go back in time and recreate the data from the past (like, if we realised at some point that in the past we'd deleted the wrong secrets and needed to recover them! It hasn't happened yet, but it's inevitable it <em>will happen</em> at some point). That's why we've created this backup solution that creates dated dumps of an SQLite database that it keeps to ensure that relevant past versions of the data can be recovered, but without storing more data than necessary (a full hard disk is never fun).</p> <p>Those among you who know about SQLite might rightly point out that an SQLite database (unlike more powerful databases) is simply a single file that can be copied to make a backup. In this case, we're using SQLite's ".backup" command to create the database backup, which we see as a mechanism for future-proofing - at some future time, it might be that the option to copy the file without corrupting the database may no longer be available, but we'd expect that the .backup command would continue to work.</p> <p>This backup process involves 3 files: the backup BASH script itself, which has all the smarts, the .conf file which defines the file paths and other details specific to your installation - like where the SQLite database actually is, and where to put the backup files - and a cron file which runs the script automatically when you tell it to run on your server.</p> <p>We've got the <a href="https://git.oeru.org/dave/sqlite-backup">whole thing</a> in our <a href="https://git.oeru.org">Gitlab instance</a>, which is the best place to learn more about how to use it - see the <a href="https://git.oeru.org/dave/sqlite-backup/blob/master/README.md">README.md</a> for installation instructions!</p> </div> </div> </div> <section class="field field-node--field-blog-comments field-name-field-blog-comments field-type-comment field-label-above comment-wrapper"> <a name="comments"></a> <div class="comment-form-wrapper"> <h2 class="comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=30&amp;2=field_blog_comments&amp;3=comment" token="LUsqyib8Nl62guCFKZHLxak9ymOGimis4ahdLWzFAfE"></drupal-render-placeholder> </div> </section> Mon, 03 Feb 2020 23:17:20 +0000 dave 30 at http://tech.oeru.org http://tech.oeru.org/automatic-versioned-backups-sqlite-docker-compose-container#comments Installing NextCloud Hub with OnlyOffice on Ubuntu 18.04 http://tech.oeru.org/installing-nextcloud-hub-onlyoffice-ubuntu-1804 <span class="field field--name-title field--type-string field--label-hidden">Installing NextCloud Hub with OnlyOffice on Ubuntu 18.04</span> <div class="field field-node--field-blog-tags field-name-field-blog-tags field-type-entity-reference field-label-above"> <h3 class="field__label">Blog tags</h3> <div class="field__items"> <div class="field__item field__item--ubuntu-linux"> <span class="field__item-wrapper"><a href="/taxonomy/term/12" hreflang="en">ubuntu linux</a></span> </div> <div class="field__item field__item--_804"> <span class="field__item-wrapper"><a href="/taxonomy/term/68" hreflang="en">18.04</a></span> </div> <div class="field__item field__item--nextcloud"> <span class="field__item-wrapper"><a href="/taxonomy/term/51" hreflang="en">nextcloud</a></span> </div> <div class="field__item field__item--onlyoffice"> <span class="field__item-wrapper"><a href="/taxonomy/term/69" hreflang="en">onlyoffice</a></span> </div> <div class="field__item field__item--mariadb"> <span class="field__item-wrapper"><a href="/taxonomy/term/48" hreflang="en">mariadb</a></span> </div> <div class="field__item field__item--docker-compose"> <span class="field__item-wrapper"><a href="/taxonomy/term/25" hreflang="en">docker compose</a></span> </div> <div class="field__item field__item--docker"> <span class="field__item-wrapper"><a href="/taxonomy/term/16" hreflang="en">docker</a></span> </div> <div class="field__item field__item--php"> <span class="field__item-wrapper"><a href="/taxonomy/term/40" hreflang="en">php</a></span> </div> <div class="field__item field__item--redis"> <span class="field__item-wrapper"><a href="/taxonomy/term/21" hreflang="en">redis</a></span> </div> <div class="field__item field__item--polls"> <span class="field__item-wrapper"><a href="/taxonomy/term/70" hreflang="en">polls</a></span> </div> <div class="field__item field__item--scheduling"> <span class="field__item-wrapper"><a href="/taxonomy/term/71" hreflang="en">scheduling</a></span> </div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">dave</span></span> <span class="field field--name-created field--type-created field--label-hidden">Tue 04/02/2020 - 09:41</span> <div class="field field-node--field-image field-name-field-image field-type-image field-label-hidden has-multiple"> <figure class="field-type-image__figure image-count-1"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2020-02/Screenshot_2020-02-11-2%20file-sample_1MB%20docx%20-%20Lane%20NextCloud.png?itok=bqhSZGni" title="Sample DOCX file being edited in open source OnlyOffice" data-colorbox-gallery="gallery-field_image-ewTrdr0Bi88" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Sample DOCX file being edited in open source OnlyOffice&quot;}"><img src="/sites/default/files/styles/medium/public/2020-02/Screenshot_2020-02-11-2%20file-sample_1MB%20docx%20-%20Lane%20NextCloud.png?itok=OW_2e1WM" width="220" height="140" alt="Sample DOCX file being edited in open source OnlyOffice" typeof="foaf:Image" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-2"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2020-02/Screenshot_2020-02-11%20Digital%20Storage%20Weight%20and%20Volume%20xlsx%20-%20Lane%20NextCloud.png?itok=luaOg5rt" title="Sample XLSX file being edited in open source OnlyOffice" data-colorbox-gallery="gallery-field_image-ewTrdr0Bi88" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Sample XLSX file being edited in open source OnlyOffice&quot;}"><img src="/sites/default/files/styles/medium/public/2020-02/Screenshot_2020-02-11%20Digital%20Storage%20Weight%20and%20Volume%20xlsx%20-%20Lane%20NextCloud.png?itok=CZcvoi7c" width="220" height="140" alt="Sample XLSX file being edited in open source OnlyOffice" typeof="foaf:Image" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-3"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2020-02/Screenshot_2020-02-11%20file-sample_1MB%20docx%20-%20Lane%20NextCloud.png?itok=GzQZ26uC" title="Another example of DOCX file being edited in open source OnlyOffice" data-colorbox-gallery="gallery-field_image-ewTrdr0Bi88" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Another example of DOCX file being edited in open source OnlyOffice&quot;}"><img src="/sites/default/files/styles/medium/public/2020-02/Screenshot_2020-02-11%20file-sample_1MB%20docx%20-%20Lane%20NextCloud.png?itok=9KcMLR4t" width="220" height="140" alt="Another example of DOCX file being edited in open source OnlyOffice" typeof="foaf:Image" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-4"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2020-02/Screenshot_2020-02-11%20Files%20-%20OERu%20NextCloud.png?itok=Zm3uP_wT" title="Sample of web-view of NextCloud files and folders." data-colorbox-gallery="gallery-field_image-ewTrdr0Bi88" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Sample of web-view of NextCloud files and folders.&quot;}"><img src="/sites/default/files/styles/medium/public/2020-02/Screenshot_2020-02-11%20Files%20-%20OERu%20NextCloud.png?itok=Bpnx2qg6" width="220" height="155" alt="Sample of web-view of NextCloud files and folders." typeof="foaf:Image" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-5"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2020-02/Screenshot_2020-02-11%20Polls%20-%20NZOSS%20Nextcloud.png?itok=Fp17si_J" title="A sample NextCloud Poll (alternative to Doodle Polls) for scheduling. Yes, it&#039;s timezone-aware!" data-colorbox-gallery="gallery-field_image-ewTrdr0Bi88" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;A sample NextCloud Poll (alternative to Doodle Polls) for scheduling. Yes, it&#039;s timezone-aware!&quot;}"><img src="/sites/default/files/styles/medium/public/2020-02/Screenshot_2020-02-11%20Polls%20-%20NZOSS%20Nextcloud.png?itok=8rpIpQne" width="220" height="155" alt="A sample NextCloud Poll (alternative to Doodle Polls) for scheduling. Yes, it&#039;s timezone-aware!" typeof="foaf:Image" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-6"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2020-02/Screenshot_2020-02-12%20ONLYOFFICE%E2%84%A2.png?itok=04xrGCN1" title="If your onlyoffice.domain server is working, this is what you should see in your browser!" data-colorbox-gallery="gallery-field_image-ewTrdr0Bi88" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;If your onlyoffice.domain server is working, this is what you should see in your browser!&quot;}"><img src="/sites/default/files/styles/medium/public/2020-02/Screenshot_2020-02-12%20ONLYOFFICE%E2%84%A2.png?itok=1I3F7Cbi" width="220" height="187" alt="If your onlyoffice.domain server is working, this is what you should see in your browser!" typeof="foaf:Image" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-7"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2020-02/Screenshot_2020-02-13%20Apps%20-%20NZOSS%20Nextcloud.png?itok=Io3v-pzT" title="The NextCloud &quot;app&quot; configuration page" data-colorbox-gallery="gallery-field_image-ewTrdr0Bi88" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;The NextCloud &quot;app&quot; configuration page&quot;}"><img src="/sites/default/files/styles/medium/public/2020-02/Screenshot_2020-02-13%20Apps%20-%20NZOSS%20Nextcloud.png?itok=VGORRtXl" width="103" height="220" alt="The NextCloud &quot;app&quot; configuration page" typeof="foaf:Image" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-8"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2020-02/Screenshot_2020-02-13%20Settings%20-%20NZOSS%20Nextcloud.png?itok=8sZGBrtG" title="The NextCloud settings page. " data-colorbox-gallery="gallery-field_image-ewTrdr0Bi88" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;The NextCloud settings page. &quot;}"><img src="/sites/default/files/styles/medium/public/2020-02/Screenshot_2020-02-13%20Settings%20-%20NZOSS%20Nextcloud.png?itok=98LUc9zv" width="220" height="163" alt="The NextCloud settings page. " typeof="foaf:Image" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-9"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2020-02/Screenshot_2020-02-13%20Settings%20-%20OnlyOffice%20-%20NZOSS%20Nextcloud_0.png?itok=KwWWWKUT" title="The &quot;OnlyOffice&quot; app configuration page for NextCloud" data-colorbox-gallery="gallery-field_image-ewTrdr0Bi88" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;The &quot;OnlyOffice&quot; app configuration page for NextCloud&quot;}"><img src="/sites/default/files/styles/medium/public/2020-02/Screenshot_2020-02-13%20Settings%20-%20OnlyOffice%20-%20NZOSS%20Nextcloud_0.png?itok=JdKSA022" width="220" height="189" alt="The &quot;OnlyOffice&quot; app configuration page for NextCloud" typeof="foaf:Image" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-10"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2020-02/Screenshot_2020-02-21%20App%20Bundles-%20OERu%20NextCloud.png?itok=3N1GtflH" title="NextCloud App Bundle configuration page." data-colorbox-gallery="gallery-field_image-ewTrdr0Bi88" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;NextCloud App Bundle configuration page.&quot;}"><img src="/sites/default/files/styles/medium/public/2020-02/Screenshot_2020-02-21%20App%20Bundles-%20OERu%20NextCloud.png?itok=c1_0PwiH" width="220" height="141" alt="NextCloud App Bundle configuration page." typeof="foaf:Image" class="image-style-medium" /> </a> </div> </figure> </div> <div class="clearfix text-formatted field field-node--body field-name-body field-type-text-with-summary field-label-hidden"> <div class="field__items"> <div class="field__item"><p>I have previously provided an <a href="/installing-nextcloud-and-collabora-office-online-docker-ubuntu-1604">in-depth explanation about NextCloud with Collabora Office Online and how we've installed it on Ubuntu 16.04</a>. This is an update both of the process, and of the technology. NextCloud is leaping from strength to strength, and seems to be <a href="https://nextcloud.com/blog/eu-governments-choose-independence-from-us-cloud-providers-with-nextcloud/">benefiting from</a> the well-founded concern held by many in the EU about data sovereignty and the market domination (and exploitation) of US-based multinationals like Amazon, Google, Microsoft, Dropbox, and others. As a collaborative, web-based front end to LibreOffice, Collabora shows great potential... but it's not anywhere near the capabilities of Google Docs...</p> <p>The same, however, is not true of a relatively new entry into the web-based collaborative productivity application space: <a href="https://www.onlyoffice.com/">OnlyOffice</a>. The application itself (for the tech focused reader, they've built an entirely <a href="https://github.com/ONLYOFFICE/">new application ecosystem</a> primarily using modern Javascript frameworks)  is impressive in both capabilities and polish. The only real caveat I've come across is that it uses, by default, the <a href="https://openstandards.nz/case-study-microsofts-ooxml-standard">fauxpen standard formats</a> developed by Microsoft rather than the true open standard formats of <a href="https://en.wikipedia.org/wiki/OpenDocument">OpenDocumentFormat</a>. But in a world where, sadly, most people don't even know what a file format is, any software that doesn't read and write the incumbent monopolist's format with great fidelity is dead in the water.  On that count, OnlyOffice is impressive.</p> <h2>NextCloud and OnlyOffice - even better together!</h2> <p>The beauty of the open source software model is that we can connect complementary applications, like NextCloud and OnlyOffice - developed by completely separate communities - to create a tightly integrated, highly functional, diverse computing platform. This combination, along with a bunch of other NextCloud "apps", is the equal of something like Google Apps (which includes Google Docs and Google Drive), but is <em>under your control, not Google's.</em> To me, that's a crucial difference. </p> <p>With the release of NextCloud 18.0.1, NextCloud has bundled OnlyOffice with it, creating something called "<a href="https://nextcloud.com/hub/">NextCloud Hub</a>". It's pretty impressive. That's what we're setting up here!</p> <h2>Setting up your own NextCloud Hub!</h2> <p>Yes, NextCloud and OnlyOffice servers on the same host.</p> <p>If you're game to run your own (and, in my experience, it's a surprisingly well behaved system) here's how you do it.</p> <p>In preparation, you'll want to have the following ready:</p> <ul><li>a Linux virtual machine or "VM" (I recommend running the current Ubuntu LTS version, or current Debian) with an external IP address and a user with sudo privileges - <a href="/setting-your-own-bitwarden-password-keeper-and-sync-server">more info on that</a>...,</li> <li>your domain name for the NextCloud instance, pointing to the IP address of your VM,</li> <li>credentials for an email address capable of sending from a remote server (usually termed an "<a href="/configuring-linux-server-send-email-postfix-smtp-server">authenticating SMTP email account</a>")</li> </ul><p>Please note: the images accompanying this howto have been pulled from several different NextCloud and OnlyOffices I maintain.</p> <h3>Secure access with SSH</h3> <p>First things first, make sure you're logged into your host (probably via SSH) as a user who has "sudo" capabilities! You need to log into the host from your local machine. We recommend setting up <a href="https://www.digitalocean.com/community/tutorials/how-to-configure-ssh-key-based-authentication-on-a-linux-server">key-based authentication</a>.</p> <h3>Firewall with UFW</h3> <p>No computer system is ever full secure - there're always exploits waiting to be found, so security is a process of maintaining vigilance. Part of that is reducing exposure - minimising your "attack surface". Use a firewall - "<a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-firewall-with-ufw-on-ubuntu-16-04" title="Uncomplicated FireWall">ufw</a>" is installed on Ubuntu by default. Make sure you've got exceptions for SSH (without them, you could lock yourself out of your machine! Doh!).</p> <p>Run the following commands to allow your Docker containers to talk to other services on your host.</p> <p><code>sudo ufw allow in on docker0<br /> sudo ufw allow from 172.0.0.0/8 to any</code></p> <p>Specifically for Docker's benefit, you need to tweak the default Forwarding rule (I use "vim" as my editor. If you don't know how to/want to use it, replace <strong>vim</strong> with <strong>nano</strong> everywhere you see it in the following - nano's easier to use for simple edits like this):</p> <p><code>sudo vim /etc/default/ufw</code></p> <p>and copy the line <code>DEFAULT_FORWARD_POLICY="DROP"</code> tweak it to look like this (commenting out the default, but leaving it there for future reference!):</p> <p><code>#DEFAULT_FORWARD_POLICY="DROP"<br /> DEFAULT_FORWARD_POLICY="ACCEPT"</code></p> <p>You also have to edit <code>/etc/ufw/sysctl.conf</code> and remove the "#" at the start of the following lines, so they look like this:</p> <p><code>sudo vim /etc/ufw/sysctl.conf</code></p> <p><code># Uncomment this to allow this host to route packets between interfaces<br /> net/ipv4/ip_forward=1<br /> net/ipv6/conf/default/forwarding=1<br /> net/ipv6/conf/all/forwarding=1</code></p> <p>and finally restart the network stack and ufw on your server<code> </code></p> <p><code>sudo service networking restart<br /> sudo service ufw restart</code></p> <h3>Installing the Nginx webserver</h3> <p>In the configuration I'm describing here, you'll need a webserver running on the server - it'll be acting as a "proxy" for the Docker-based Nginx instance described below. I like the efficiency of Nginx and clarity of Nginx configurations over those of Apache and other open source web servers. Here's how you install it.</p> <p><code>sudo apt-get install nginx-full</code></p> <p>To allow nginx to be visible via ports 80 and 443, run</p> <p><code>sudo ufw allow "Nginx Full"</code></p> <p><strong>Note</strong>: make sure your hosting service is not blocking these ports at some outer layer (depending on who's providing that hosting service you may have to set up port forwarding).</p> <h3>Installing MariaDB</h3> <p>MariaDB is effectively a drop-in alternative to MySQL and we prefer it because it's not controlled by Oracle and has a more active developer community. On Ubuntu, MariaDB pretends to be MySQL for compatibility purposes, so don't be weirded out by the interchangeable names below. Install the server and the client like this.</p> <p><code>sudo apt-get install mariadb-server-10.0 mariadb-client-10.0</code></p> <p>You need to set a root (admin) user password - you might want to create a /root/.my.cnf file containing the following (replacing YOURPASSWORD) to let you access MariaDB without a password from the commandline<code>:</code></p> <p><code>[client]<br /> user=root<br /> password=YOURPASSWORD</code></p> <p>You should now be able to type "mysql" at the command prompt</p> <p>Tweak the configuration so that it's listening on</p> <p><code>sudo vim /etc/mysql/mariadb.conf.d/50-server.cnf </code></p> <p>and copy the bind-address line and adjust so it looks like this - we want MariaDB to be listening on all interfaces, not just localhost (127.0.0.1)...</p> <p><code># Instead of skip-networking the default is now to listen only on<br /> # localhost which is more compatible and is not less secure.<br /> #bind-address           = 127.0.0.1<br /> bind-address            = 0.0.0.0</code></p> <p>Then restart MariaDB:</p> <p><code>sudo service mysql restart</code></p> <p>It should now be listening on port 3306 on all interfaces, i.e. 0.0.0.0.</p> <p>Now set up the database which will hold NextCloud's data. Log into the MySQL client on the host (if you've created a .my.cnf file in your home directory as describe above, you won't need to enter your username and password):</p> <p><code>mysql -u root -p</code></p> <p>Enter your root password when prompted. It's also a good idea to gin up a password for your "nextcloud" database user. I usually use pwgen (<code>sudo apt-get install pwgen</code>) - for example running this command will give you a single 19 character password without special characters (just numbers and letters):</p> <p><code>pwgen -s 19 1</code></p> <p>Giving you something like this (but if it's truly random, almost <em>certainly not exactly </em>this):</p> <p>bYIOSrvR9aGwL5FRGFU</p> <p>At the prompt (which will look something like <code>MariaDB [(none)]&gt;</code>) enter the following lines (putting your password in place of [passwd]):</p> <p><code>CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;<br /> CREATE USER "nextcloud"@"%" IDENTIFIED BY "[passwd]";<br /> GRANT ALL ON nextcloud.* to "nextcloud"@"%";<br /> FLUSH PRIVILEGES;</code></p> <p>Then enter \q to exit.</p> <h2>Prepare your Docker Compose host</h2> <p>We make use of the NextCloud community's <a href="https://hub.docker.com/_/nextcloud/" title="Documentation for the reference NextCloud Docker container.">stable Docker container</a> which they keep up to date. Similarly, the OnlyOffice developers maintain a Docker container, too. We will run them both on this same server as separate services via <a href="https://docs.docker.com/compose/">Docker Compose</a>. The two sets of Docker containers will look like this:</p> <ol><li>a suite of NextCloud containers: <ol><li>the main PHP-FPM container (which provides most of the functionality for NextCloud using the PHP scripting engine,</li> <li>an identical container to the PHP one which runs the cron service (which does periodic administrative tasks relevant to NextCloud)</li> <li>a Redis container (which provides performance improving caching for NextCloud), and</li> <li>an Nginx webserver container which makes it easier to manage the configuration and paths of the NextCloud instance. It means that on the hosting server, we only need to run a proxying web server, which is easy.</li> </ol></li> <li>the single OnlyOffice container which, despite the Docker convention of each container running only a single services, runs the whole OnlyOffice stack, which includes PostgreSQL, Nginx, Rabbit-MQ, Python, and NodeJS.</li> </ol><p>The way I prefer to implement this set of containers is to use:</p> <p><code>sudo apt-get install docker-compose </code></p> <p>to set up the entire Docker and Docker Compose system on your server.</p> <p>Then set up a place for your Docker containers (replace "me" with your non-root username on the server) and the associated persistent data (your Docker containers should hold <em>no</em> important data - you should be able to delete and recreate them entirely without losing any important data or configuration):</p> <p><code>sudo mkdir /home/data</code><br /><code>sudo mkdir /home/data/nextcloud<br /> sudo mkdir /home/data/nextcloud-nginx<br /> sudo mkdir /home/data/nextcloud-redis</code><br /><code>sudo mkdir /home/data/onlyoffice</code><br /><code>sudo mkdir /home/docker<br /> sudo mkdir /home/docker/nextcloud</code><br /><code>sudo chown -R me:me /home/docker</code></p> <h2>NextCloud Install</h2> <p>First, let's set up NextCloud (this also installs the OnlyOffice server):</p> <p><code>cd /home/docker/nextcloud</code></p> <p>Here's an example of the required docker-compose.yml file (you can create this via a text editor like "nano" which should be pre-installed on any VM these days (or use my preferred, but less intuitive, editor, vim) <code>nano docker-compose.yml</code> in the /home/docker/nextcloud directory):</p> <p><code>version: '3'<br /> services:<br />   nginx:<br />     container_name: nginx-server<br />     image: nginx<br />     ports:<br />       - 127.0.0.1:8082:80<br />     volumes:<br />       - /home/data/nextcloud-nginx/nginx/nginx.conf:/etc/nginx/nginx.conf:ro<br />       - /home/data/nextcloud:/var/www/html<br />     links:<br />       - app<br />     environment:<br />       - VIRTUAL_HOST<br />     restart: unless-stopped      <br />   app:<br />     container_name: app-server<br />     image: nextcloud:fpm<br />     stdin_open: true<br />     tty: true<br />     links:<br />       - redis<br />     expose:<br />       - '80'<br />       - '9000'<br />     volumes:<br />       - /home/data/nextcloud:/var/www/html<br />     restart: unless-stopped      <br />   cron:<br />     image: nextcloud:fpm<br />     volumes:<br />       - /home/data/nextcloud:/var/www/html<br />     user: www-data<br />     entrypoint: |<br />       bash -c 'bash -s &lt;&lt;EOF<br />       trap "break;exit" SIGHUP SIGINT SIGTERM<br />       while /bin/true; do<br />         /usr/local/bin/php /var/www/html/cron.php<br />         sleep 900<br />       done<br />       EOF'<br />     restart: unless-stopped      <br />   redis:<br />     image: redis:alpine<br />     volumes:<br />       - /home/data/nextcloud-redis:/data<br />     restart: unless-stopped<br />   onlyoffice-document-server:<br />     container_name: onlyoffice-document-server<br />     image: onlyoffice/documentserver:latest<br />     stdin_open: true<br />     tty: true<br />     restart: unless-stopped<br />     expose:<br />       - '80'<br />       - '443'<br />     volumes:<br />       - /home/data/onlyoffice/data:/var/www/onlyoffice/Data<br />       - /home/data/onlyoffice/log:/var/log/onlyoffice</code></p> <p>The "port" specified above, 8082 for <code>nginx</code> is arbitrary - I picked it to ensure it doesn't don't conflict with ports being used by other containers on my server - you can use these if you want, or use <code>sudo netstat -punta</code> to see what ports are currently claimed by other services on your server (if there are any) and pick one that doesn't clash! If it scroll past too fast, you can pipe it into less to allow you to scroll and search like this: <code>sudo netstat -punta | less</code> - hit "q" to exit or "/" to initiate a text search.</p> <p>You will also need to provide the "nginx.conf" file referenced in the nginx section of the file. Do that by using your editor, e.g. <code>nano nginx.conf</code>, and enter this content (you shouldn't need to alter anything):</p> <p><code>user  www-data;</code></p> <p><code>worker_processes  1;</code></p> <p><code>error_log  /var/log/nginx/error.log warn;<br /> pid        /var/run/nginx.pid;</code></p> <p><code>events {<br />     worker_connections  1024;<br /> }</code></p> <p><code>http {<br />     upstream backend {<br />         server app-server:9000;<br />     }</code></p> <p><code>    include       /etc/nginx/mime.types;<br />     default_type  application/octet-stream;</code></p> <p><code>    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '<br />                       '$status $body_bytes_sent "$http_referer" '<br />                       '"$http_user_agent" "$http_x_forwarded_for"';</code></p> <p><code>    access_log  /var/log/nginx/access.log  main;</code></p> <p><code>    sendfile        on;<br />     #tcp_nopush     on;</code></p> <p><code>    keepalive_timeout  65;</code></p> <p><code>    map $http_host $this_host {<br />         "" $host;<br />         default $http_host;<br />     }</code></p> <p><code>    map $http_x_forwarded_proto $the_scheme {<br />         default $http_x_forwarded_proto;<br />         "" $scheme;<br />     }</code></p> <p><code>    map $http_x_forwarded_host $the_host {<br />         default $http_x_forwarded_host;<br />         "" $this_host;<br />     }</code></p> <p><code>    server {<br />         listen 80;</code></p> <p><code>        # Add headers to serve security related headers<br />         add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";<br />         add_header X-Content-Type-Options nosniff;<br />         add_header X-XSS-Protection "1; mode=block";<br />         add_header X-Robots-Tag none;<br />         add_header X-Download-Options noopen;<br />         add_header X-Permitted-Cross-Domain-Policies none;</code></p> <p><code>        root /var/www/html;<br />         client_max_body_size 10G; # 0=unlimited - set max upload size<br />         fastcgi_buffers 64 4K;</code></p> <p><code>        gzip off;</code></p> <p><code>        index index.php;<br />         error_page 403 /core/templates/403.php;<br />         error_page 404 /core/templates/404.php;</code></p> <p><code>        rewrite ^/.well-known/carddav /remote.php/dav/ permanent;<br />         rewrite ^/.well-known/caldav /remote.php/dav/ permanent;</code></p> <p><code>        location = /robots.txt {<br />             allow all;<br />             log_not_found off;<br />             access_log off;<br />         }</code></p> <p><code>        location ~ ^/(build|tests|config|lib|3rdparty|templates|data)/ {<br />             deny all;<br />         }</code></p> <p><code>        location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {<br />             deny all;<br />         }</code></p> <p><code>        location / {<br />             rewrite ^/remote/(.*) /remote.php last;<br />             rewrite ^(/core/doc/[^\/]+/)$ $1/index.html;<br />             try_files $uri $uri/ =404;<br />         }</code></p> <p><code>        location ~* ^/ds-vpath/ {<br />             rewrite /ds-vpath/(.*) /$1  break;<br />             proxy_pass http://onlyoffice-document-server;<br />             proxy_redirect     off;</code></p> <p><code>            client_max_body_size 100m;</code></p> <p><code>            proxy_http_version 1.1;<br />             proxy_set_header Upgrade $http_upgrade;<br />             proxy_set_header Connection "upgrade";</code></p> <p><code>            proxy_set_header Host $http_host;<br />             proxy_set_header X-Real-IP $remote_addr;<br />             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;<br />             proxy_set_header X-Forwarded-Host $the_host/ds-vpath;<br />             proxy_set_header X-Forwarded-Proto $the_scheme;<br />             #proxy_set_header X-Forwarded-Proto 'https';<br />         }</code></p> <p><code>        location ~ \.php(?:$|/) {<br />             fastcgi_split_path_info ^(.+\.php)(/.+)$;<br />             include fastcgi_params;<br />             fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;<br />             fastcgi_param PATH_INFO $fastcgi_path_info;<br />             fastcgi_param HTTPS off;<br />             fastcgi_param modHeadersAvailable true; #Avoid sending the security headers twice<br />             fastcgi_pass backend;<br />             fastcgi_intercept_errors on;<br />         }</code></p> <p><code>        # Adding the cache control header for js and css files<br />         # Make sure it is BELOW the location ~ \.php(?:$|/) { block<br />         location ~* \.(?:css|js)$ {<br />             add_header Cache-Control "public, max-age=7200";<br />             # Add headers to serve security related headers<br />             add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";<br />             add_header X-Content-Type-Options nosniff;<br />             add_header X-Frame-Options "SAMEORIGIN";<br />             add_header X-XSS-Protection "1; mode=block";<br />             add_header X-Robots-Tag none;<br />             add_header X-Download-Options noopen;<br />             add_header X-Permitted-Cross-Domain-Policies none;<br />             # Optional: Don't log access to assets<br />             access_log off;<br />         }</code></p> <p><code>        # Optional: Don't log access to other assets<br />         location ~* \.(?:jpg|jpeg|gif|bmp|ico|png|swf)$ {<br />             access_log off;<br />         }</code><br /><code>    }<br /> }</code></p> <p>That should be all the configuration you need to make the Docker containers go.</p> <h2>Configuring Nginx to proxy NextCloud and OnlyOffice</h2> <p>The next step is configuring the local nginx proxy servers for NextCloud and OnlyOffice using the nginx instance you installed earlier. That's what responds to the domain name you choose for this service. In our case, the name is <a href="https://docs.oeru.org">https://docs.oeru.org</a> - you can have a look at it to see what you should be seeing when you first start things up! We use <a href="https://letsencrypt.org" title="This is an incredible free and open source service, that is single-handedly making the web a much safer place.">Let's Encrypt</a> to provide secure hosting - <a href="/protecting-your-users-lets-encrypt-ssl-certs">here're my Let's Encrypt instructions</a> on setting it up. The key thing to realise is that your "certificates" need to exist for Nginx to restart with the new configurations below - use the "commenting out the intervening lines" trick mentioned in my instructions to bootstrap the creation of your secure certificates!</p> <p>To configure the proxy, you need to create this configuration file in your /etc/nginx/sites-available/ directory.</p> <h3>NextCloud Proxy Configuration</h3> <p>Create a file with a meaningful name for your NextCloud Proxy, perhaps based on the domain name you've chosen (our file for docs.oeru.org is called "docs") using the same editing approach as the last few (although this is in a different directory) for example <code>sudo nano /etc/nginx/sites-available/nextcloud</code> with the following contents, replacing <code>[nextcloud.domain]</code> with your selected domain name, but leave off the [ ] (those are just there to make sure nginx errors if you've missed replacing any) - and the port number 8082 if you've opted to change to a different one!:</p> <p><code>server {<br />     listen 80;<br />     listen [::]:80;<br />     server_name <strong>[nextcloud.domain]</strong>;</code></p> <p><code>    include includes/letsencrypt.conf;</code></p> <p><code>    # enforce https<br />     location / {<br />         return 302 https://$server_name$request_uri;<br />     }<br /> }</code></p> <p><code>server {<br />     listen 443 ssl;<br />     listen [::]:443 ssl;<br />     #listen 127.0.0.1:443 ssl;</code></p> <p><code>    server_name <strong>[nextcloud.domain]</strong>;</code></p> <p><code>    ## Access and error logs.<br />     access_log /var/log/nginx/<strong>[nextcloud.domain]</strong>_access.log;<br />     error_log /var/log/nginx/<strong>[nextcloud.domain]</strong>_error.log;</code></p> <p><code>    ssl_certificate /etc/letsencrypt/live/<strong>[nextcloud.domain]</strong>/fullchain.pem;<br />     ssl_certificate_key /etc/letsencrypt/live/<strong>[nextcloud.domain]</strong>/privkey.pem;</code></p> <p><code>    ssl on;<br />     # from http://axiacore.com/blog/enable-perfect-forward-secrecy-nginx/<br />     ssl_session_cache shared:SSL:10m;<br />     ssl_session_timeout  10m;<br />     # limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;<br />     # forward secrecy settings<br />     ssl_protocols TLSv1 TLSv1.1 TLSv1.2;<br />     ssl_prefer_server_ciphers on;<br />     ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";<br />     ssl_dhparam /etc/ssl/certs/dhparam.pem;</code></p> <p><code>    #location = /robots.txt {<br />     #    allow all;<br />     #    log_not_found off;<br />     #    access_log off;<br />     #}</code></p> <p><code>    # The following 2 rules are only needed for the user_webfinger app.<br />     # Uncomment it if you're planning to use this app.<br />     rewrite ^/.well-known/host-meta /public.php?service=host-meta last;<br />     rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;</code></p> <p><code>    # The following rule is only needed for the Social app.<br />     # Uncomment it if you're planning to use this app.<br />     rewrite ^/.well-known/webfinger /public.php?service=webfinger last;</code></p> <p><code>    location ^~ / {<br />         proxy_pass http://127.0.0.1:<strong>8082</strong>;<br />         proxy_set_header Upgrade $http_upgrade;<br />         proxy_set_header Connection "Upgrade";<br />         proxy_set_header Host $http_host;<br />         proxy_read_timeout 36000s;<br />         proxy_buffering off;<br />         proxy_max_temp_file_size 15000m;<br />     }<br />     client_max_body_size 1G;<br />     fastcgi_buffers 64 4K;<br />     add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";<br />     # Remove X-Powered-By, which is an information leak<br />     fastcgi_hide_header X-Powered-By;<br /> }</code></p> <p>Note: you'll need to create the file cited in the proxy configration: <code>/etc/ssl/certs/dhparam.pem </code></p> <p>You can do this as follows (install the necessary software, backup any possible existing version as a matter of prudence, and create a new one):</p> <p><code>sudo apt update &amp;&amp; sudo apt install openssl<br /><span class="pun">sudo [<span class="pln"> </span>-</span><span class="pln">f </span><span class="str">"</span>/etc/ssl/certs/dhparam.pem<span class="str">"</span><span class="pln"> </span><span class="pun">]</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> sudo mv </span>/etc/ssl/certs/dhparam.pem /etc/ssl/certs/dhparam.pem</code>.bak<br /> sudo  openssl dhparam -rand - 2048 &gt; <code>/etc/ssl/certs/dhparam.pem</code></p> <p>Once those are created, you have to make sure that they're "enabled" (replacing with your file names, of course):</p> <p><code>sudo cd /etc/nginx/sites-enabled<br /> sudo ln -sf ../sites-available/nextcloud .</code></p> <p>To confirm that there aren't any typos or issues that might make nginx unhappy, run</p> <p><code>sudo nginx -t</code></p> <p>If all's well, get nginx to reread its configuration with the new files (if not, it might be because you missed replacing one of the [tokens]):</p> <p><code>sudo service nginx reload</code></p> <h2>Firing up your NextCloud!</h2> <p>Phew - congratulations on getting here! We've reached the moment of truth where we need to see if this whole thing will work!</p> <p>We need to make sure we're back in the NextCloud Docker directory we set up:</p> <p><code>cd /home/docker/nextcloud</code></p> <p>and then we need to try running our docker-compose script to "pull" in the pre-built Docker containers we've specified in our docker-compose.yml file:</p> <p><code>docker-compose pull</code></p> <p>All going well, after a few minutes (longer or shorter depending on the speed of your server's connection) you should have download the Nginx, Redis, and NextCloud Docker images. Then you can run:</p> <p><code>docker-compose up -d &amp;&amp; docker-compose logs -f</code></p> <p>This will attempt to start up the containers (bringing them "up" in daemon mode, thus the -d) and then show you a stream of log messages from the containers, preceded by the container name. This should help you debug any problems that occur during the process (ideally, none).</p> <p>Once you see log messages streaming past, and no obvious "container exited" or other error messages (which will usually contain the word "error" a lot), you should be able to point your browser at your selected domain name and bring it up in your browser! Just point your web browser at <code>https://nextcloud.domain</code> (replacing with your domain, of course - the https assumes you've got your Let's Encrypt certificate set up - I recommend doing that first).</p> <h3>Configuring database access</h3> <p>On doing so, if all is well, you should be directed through the database set up process for your NextCloud instance. Your details should be:</p> <p>database IP: 172.17.0.1 - this is the default IP of the Docker host server.<br /> database name: nextcloud<br /> database user: nextcloud<br /> database password: (the one you came up with above)</p> <h3>Configuring the Admin user</h3> <p>Once that's set and working, NextCloud will install all the relevant database tables and initial data. You'll be asked to set up an <em>admin user</em> account, which can be "admin" (you could make it something different to help stymie nefarious probes that assume you've got a user called "admin" - but don't forget what you've called it!) and some strong password you create (you can use the pwgen utility you used earlier) - I'd recommend recording it somewhere. I would <em>not</em> recommend making your own account, in your name, the main admin account. Instead, I recommend creating a second account, <em>with administrator privileges</em>, for yourself, but leave the admin account purely for administrative activities.</p> <h3>Configuring Outgoing Email</h3> <p>To allow your NextCloud instance to send outgoing email, so that your site can alert you to security updates that need to be applied, or so that users can request a replacement password if they've forgot theirs, you'll need an <em>authenticating SMTP account</em> somewhere. Most of you already have one. You'll probably want to set up a dedicated email address for this server somewhere, perhaps something like "<a href="mailto:nextcloud@your.domain">nextcloud@your.domain</a>" or similar, with a username (often just the email address) and a password. You'll need the following details:</p> <p>SMTP server : an IP address or a domain name<br /> SMTP username: a username or an email address<br /> SMTP password: a strong password already configured for the username on that server<br /> SMTP login security: whether login is via TLS, SSL, or unsecure (!!), and<br /> SMTP login method: plain, encrypted, "login" or some other value.</p> <p>You should be able to test your email settings to make sure the details you've entered are valid. If you need to adjust these settings later, you can go to the admin menu (top right of the web browser interface) and go to Admin-&gt;Additional Settings  - should have a path of <code>https://nextcloud.domain/settings/admin/additional</code></p> <h2>Setting up OnlyOffice</h2> <p>The OnlyOffice server should already be running - if you point your browser at <code>https://nextcloud.domain/ds-vpath/</code> you should see something like the "Document Server is running" (with a big green "tick") page included in the images accompanying this article.</p> <h3>Configuring OnlyOffice Integration with NextCloud</h3> <p>Once you're logged in to NextCloud as your own user, looking at your own default folders, you can start having a look around. You should have an "admin" menu (assuming you've created your user with Administrator privileges) at the top right of the web interface. If you go to Apps, you can install the new "Hub bundle" available under the "App bundles" option (see attached image). If you don't want the whole bundle you can just use the search box to search for "OnlyOffice" or go to the "Office &amp; text" App category and enable the OnlyOffice "official" app, at which point it will automatically download the latest version of the connector app and install it (it should appear in your /home/data/nextcloud/apps directory)</p> <p>Once you've done that, go to your top right menu again, selecting Admin, and you should see "OnlyOffice" as an option in the left column (which starts with "Basic settings"). Selecting that, you'll need to enter the following:</p> <ul><li> "Document Editing Service address":<code> /ds-vpath/</code></li> <li><code>"</code>Secret key": (leave blank)</li> <li> Under "Advanced server settings" <ul><li> <p class="onlyoffice-header">"Document Editing Service address for internal requests from the server": <code>http://onlyoffice-document-server/</code></p> </li> <li> <p class="onlyoffice-header">"Server address for internal requests from the Document Editing Service": <code>http://nginx-server/</code></p> </li> </ul></li> </ul><p>When you're done, click "Save".</p> <p>You can also select formats you'd like OnlyOffice to open and edit files of those types are clicked or created. I've selected the following: doc, docx, odp, ods, odt, ppt, pptx, xls, xlsx, and in the second section: csv and txt.</p> <p>You can also make other editor customisations as you desire. The only Editor customisation setting I <em>haven't</em> selected is "Display Chat menu button" because NextCloud Hub provides an integrated Chat service, making this one within OnlyOffice an unnecessary distraction.</p> <p>Once finished configuring, you should have the ability to go back to the home of your NextCloud install, which should show you your top-level folders. If you click the "+" next to the home icon (top left of the folder pane) you should now have the option to create (in addition to "Upload file", "New folder", "New text file") a "New Document", "New Spreadsheet", and "New Presentation". Clicking those should give you the OnlyOffice interface for the designated content type.</p> <p>Similarly, you can use the "Upload file" to upload a document in a format that is supported by OnlyOffice. Once uploaded, clicking on the filename should open it for editing in the appropriate OnlyOffice interface.</p> <p>It is saved as it is changed, so you shouldn't need to save it explicitly.</p> <h2>Keeping the whole thing up-to-date</h2> <p>So, as you're no doubt aware, both NextCloud and OnlyOffice are always being improved and updated. I certainly encourage you to keep your installations up-to-date.</p> <p>While you'll periodically be alerted that NextCloud <strong>apps</strong> have available updates (these can be upgraded through the browser interface) updates to the NextCloud and OnlyOffice systems themselves need to be undertaken by upgrading their containers. Luckily it's easy to do (although I strongly urge you to ensure you have a very recent backup of both database and uploaded files - they're the files in /home/data/nextcloud/data and /home/data/onlyoffice/ (note, backups of OnlyOffice are complicated somewhat by the fact that you can't reliably back up running PostgreSQL instance simply by backing up its files - see a solution below):</p> <p>Updating the container should be as easy as either doing another</p> <p><code>docker-compose pull </code></p> <p>and then shutting down Docker container via a</p> <p><code>docker-compose up -d</code></p> <p>which will remove any old containers (this won't remove any data you want to save if you followed the directions above! But remember to do it in the right directory!) and start up the new versions you've just pulled.</p> <p>Use <code>docker-compose logs -f</code> to watch the logs - you'll likely see useful debugging information in the unlikely event that something goes wrong in the upgrade process.</p> <h2>Backing up NextCloud</h2> <p>To back up your instance on your server, you need two things: a file system backup of your /home/data/nextcloud directory, and database dumps of your database.</p> <p>There're lots of ways to back up your files (I've recently updated to using a system called Restic to make off-server incremental encrypted backups - I plan to document this in a future howto! - although there're <a href="https://www.howtoforge.com/linux_rdiff_backup">other documented approaches</a> - leave a comment below if you'd like to learn more about my approach!).</p> <p>Backing up your MariaDB databases is as easy installing automysqlbackups:</p> <p><code>sudo apt install automysqlbackups</code></p> <p>You'll find daily versioned dumps of your MariaDB database(s) in /var/lib/automysqlbackups on your VM host's filesystem. To run an ad hoc backup (which will replace the previous backup from that day, if there is one) just run</p> <p><code>sudo automysqlbackups</code></p> <h2>Backup OnlyOffice</h2> <p>Along with backing up the files in your /home/data/onlyoffice directory, you'll also want a proper "dump" of your PostgreSQL backup (you can write simple bash scripts to do this regularly, automatically), particularly prior to doing an upgrade (to allow for recovery if something goes badly wrong, which is always possible). You can achieve this by going to</p> <p><code>cd /home/docker/onlyoffice</code></p> <p>and running this</p> <p><code>DATE=`date +%Y%m%d` &amp;&amp; FILE=/home/data/onlyoffice/backup/fullbackup-${DATE}.sql &amp;&amp; docker-compose exec onlyoffice sudo -u postgres pg_dumpall &gt; ${FILE} &amp;&amp; gzip ${FILE}</code></p> <p>which will assign the current date to DATE, the relevant filename to FILE, and then put the backup SQL into a dated file called $FILE and compress the result with gzip :)</p> <p>At some point, I'll modify my normal versioned dated database backup scripts to cater for this solution and make the result available on <a href="https://git.oeru.org">https://git.oeru.org</a> - in the meantime, you can use the above before you do a backup and manually delete older backups if they start taking up too much space (or, better still, write your own clever script that does it automatically and let me know about it!).</p> </div> </div> </div> <section class="field field-node--field-blog-comments field-name-field-blog-comments field-type-comment field-label-above comment-wrapper"> <a name="comments"></a> <div class="comment-form-wrapper"> <h2 class="comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=29&amp;2=field_blog_comments&amp;3=comment" token="9kYJqJN_nL4QNtjWT8YsPb_jXY2Bv9NrASHDdb5feiU"></drupal-render-placeholder> </div> </section> Mon, 03 Feb 2020 20:41:16 +0000 dave 29 at http://tech.oeru.org http://tech.oeru.org/installing-nextcloud-hub-onlyoffice-ubuntu-1804#comments Configuring a Linux server to send email via the Postfix SMTP server using an external authenticating SMTP host http://tech.oeru.org/configuring-linux-server-send-email-postfix-smtp-server-using-external-authenticating-smtp-host <span class="field field--name-title field--type-string field--label-hidden">Configuring a Linux server to send email via the Postfix SMTP server using an external authenticating SMTP host</span> <div class="field field-node--field-blog-tags field-name-field-blog-tags field-type-entity-reference field-label-above"> <h3 class="field__label">Blog tags</h3> <div class="field__items"> <div class="field__item field__item--ubuntu-linux"> <span class="field__item-wrapper"><a href="/taxonomy/term/12" hreflang="en">ubuntu linux</a></span> </div> <div class="field__item field__item--postfix"> <span class="field__item-wrapper"><a href="/taxonomy/term/66" hreflang="en">postfix</a></span> </div> <div class="field__item field__item--smtp"> <span class="field__item-wrapper"><a href="/taxonomy/term/67" hreflang="en">smtp</a></span> </div> <div class="field__item field__item--_804"> <span class="field__item-wrapper"><a href="/taxonomy/term/68" hreflang="en">18.04</a></span> </div> <div class="field__item field__item--free--open-source"> <span class="field__item-wrapper"><a href="/taxonomy/term/6" hreflang="en">free &amp; open source</a></span> </div> <div class="field__item field__item--foss"> <span class="field__item-wrapper"><a href="/taxonomy/term/10" hreflang="en">foss</a></span> </div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">dave</span></span> <span class="field field--name-created field--type-created field--label-hidden">Fri 20/09/2019 - 14:08</span> <div class="clearfix text-formatted field field-node--body field-name-body field-type-text-with-summary field-label-hidden"> <div class="field__items"> <div class="field__item"><p>Just about any and every server needs to be able to send email - whether it's end-user-email, like password recovery services for a website to emails to system administrators reporting on the status of system backups and errors. The problem is that it's <em>non trivial</em> (understatement) to set up a mail server properly.</p> <p>This howto assumes you have a Linux server (these instructions are for Ubuntu 18.04, although it should work on earlier versions of Ubuntu server and Debian Linux without many changes, and the concepts will be very similar on other Linuxen) with a static IP address, with one or more fully-qualified-domain-names (fdqn) pointing at that address, and you have SSH-based access to it. I've <a href="/setting-your-own-bitwarden-password-keeper-and-sync-server">previously provided tips</a> on how to get to this stage.</p> <h2>MailCow for authenticating SMTP</h2> <p>To send email, you need access to a server, somewhere on the Internet, that provides the <a href="https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol">Simple Mail Transfer Protocol</a> (SMTP) service. It's an open standard, and for most of the history of the Internet, email services have been mostly provided by Free and Open Source Software (FOSS) tools - the first SMTP was called "<a href="https://en.wikipedia.org/wiki/Sendmail">Sendmail</a>" and it was fully FOSS, and it's still in use today (although it has mostly been superseded by faster, more secure systems, the best of which are also FOSS).</p> <p>At the OERu, we use the <a href="https://mailcow.github.io/mailcow-dockerized-docs/" title="Dockerised MailCow">Docker-based installation of the amazing, completely FOSS MailCow project</a> to provide our organisational email services. I might cover that set up in a future howto here, because MailCow makes an otherwise almost intractable problem... much more tractable. Having set up MailCow set up means we can offer "full service" email for any number of domains and users and aliases with all the bells and whistles including incoming and outgoing mail with all the virus scanning (we don't really need it because we use Linux desktops, but for other folks it's useful I guess) and dynamic spam filtering services you'd expect from a much larger operation: <a href="https://mailcow.email/">Team MailCow</a> have done an amazing job in pulling together a comprehensive set of FOSS applications to provide all the conceivable requirements of a full-fledged, multi-domain email system, including shared calendaring, contacts, and webmail. A great companion to your organisation's MailCow server would be a <a href="/setting-your-own-bitwarden-password-keeper-and-sync-server">BitWarden password safe</a> server... just sayin'. With a tiny bit of configuration cleverness, you could even run them both simultaneously on the same server... but that's a story for another day.</p> <p>So, now, assuming that we have a MailCow server or a functionally equivalent SMTP service available, we have the option of "authenticated SMTP" for outgoing email using credentials we can set up. For example, in MailCow, we can specify a domain we host, like say <strong>oeru.org</strong> (and for which we've defined an MX record and a few other relevant records as guided by MailCow administrative web interface). On top of that, we can specify a mailbox for a dedicated "send stuff from remote relay hosts" email address using that domain, like <strong><a href="mailto:smtp@oeru.org">smtp@oeru.org</a></strong>, with a strong password. With that, we can <em>securely </em>send email using that email address as the username and that password from <em>anywhere we have access to the Internet</em>.</p> <p>The <strong>only tricky part</strong> is that we have to ensure that whatever "reply to" email address we specify from our applications, say <strong><a href="mailto:notifications@tech.oeru.org">notifications@tech.oeru.org</a></strong>, is using a domain we <em>also host on the same server, </em>and that there's an <em>email alias</em> of that email address defined and set as "allow to send from <a href="mailto:smtp@oeru.org">smtp@oeru.org</a>" in the MailCow interface. If we haven't made sure of that, our mail server is likely to reject sending emails with that "mismatching" email address. This is a basic spam deterrence measure, which is for the best, despite sometimes making a email system administrator's life harder.</p> <p>Once we've got that (and it's easy once you've done it once or twice - I'm mostly writing this down now so I don't have to try to re-remember every time I need to set up a new server - and I hope it helps others, too), we can set up any server we control to send secure (and spam-filter-resilient) email. For what it's worth, too, MailCow uses Postfix as its SMTP server component (there're a bunch of other components, too).</p> <h2>Postfix SMTP with SmartHost</h2> <p>The first thing you need to do to create a postfix smarthost is to install the postfix application on a new server (this assumes you're logged in with a user who has "sudo" - aka admin - permissions):</p> <p><code>sudo apt update &amp;&amp; sudo apt install postfix bsd-mailx</code></p> <p>During the install, you'll be asked to select a bunch of configuration parameters. Select the defaults except:</p> <ul><li>Select "Internet Site with Smarthost",</li> <li>fill in the domain name for your server,</li> <li>the domain name and port (in the form <code>[smtp server domain]:[port]</code>, e.g. <code>smtp.oeru.org:587</code> ) of your "smarthost" who'll be doing the authenticating SMTP for you, and</li> <li>the email address to which you want to receive system-related messages.</li> </ul><p>After that's done, you can proceed.</p> <h2>Next Steps</h2> <p>For the rest of this howto, you'll need to do the following. First, select your text editor. I use vim, but if you're new to the command line, I recommend using nano - it's more straightforward:</p> <p><code>EDIT=`which nano`</code> or <code>EDIT=`which vim`</code></p> <p><code>sudo $EDIT /etc/aliases</code></p> <p>We need to make sure the "root" user points to a real email address. Add a line at the bottom which says (replacing [your email] with <em>your email :) </em>)</p> <p><code>root: [your email]</code></p> <p>After which you'll need to convert the aliases file into a form that postfix can process, simply by running this:</p> <p><code>sudo newaliases</code></p> <p>Then we have to define the authentication credentials required to convince your mail server that you're you!</p> <p><code>sudo $EDIT /etc/postfix/relay_password</code></p> <p>The resulting file only needs one line with three bits of information:</p> <p><code>[smtp server domain] [user name]:[password]</code></p> <p>for example:</p> <p><code>smtp.oeru.org smtp@oeru.org:SomeObscurePassw0rd</code></p> <p>Then save the file and, like the aliases file, run the conversion process (which uses a slightly different mechanism):</p> <p><code>sudo postmap /etc/postfix/relay_password</code></p> <p>Finally, we'll edit the main configuration file for Postfix to tell it about all this stuff:</p> <p><code>sudo $EDIT /etc/postfix/main.cf</code></p> <p>If your SMTP server uses port 25 (the default for <em>unencrypted</em> SMTP) you don't have to change anything, although most people nowadays prefer to use StartTLS or otherwise encrypted transport to at least ensure that your SMTP authentication details (<em>at least</em>) are transferred encrypted. That means using port 587 or 465. If you're using either of those ports, find the "relayhost = [your server name]" line... and add your port number after a colon, like this <code>relayhost = smtp.oeru.org:465 </code></p> <p>Next, add the following lines at the bottom of the file:</p> <p><code># added to configure accessing the relay host via authenticating SMTP<br /> smtp_sasl_auth_enable = yes<br /> smtp_sasl_password_maps = hash:/etc/postfix/relay_password<br /> smtp_sasl_security_options =<br /> smtp_tls_security_level = encrypt</code></p> <p>Save the file, and then check that your syntax is correct:</p> <p><code>sudo postfix check</code></p> <p>If it is (running the command returns no errors, and it might not return anything at all - that's a good thing!), then you can run</p> <p><code>sudo postfix reload</code></p> <p>to get postfix to reload its configurations and you can test out your new smarthost-configured SMTP server!</p> <p>If not, the output of the check command will usually give you a helpful insight into what is wrong with your configuration... you'll also find that looking at the mail log is very helpful and offers great insights:</p> <p><code>sudo less +G /var/log/mail.log</code></p> <p>and if you're not able to fix it based on those, you'll find postfix is widely documented and has rich set of easily discoverable resources out there on the web - a search engine is your best resource!</p> <h2>Testing your outgoing email</h2> <p>By default, a command line application called "mail" is installed as part of the bsd-mailx package we installed alongside postfix. You can use it to send test email from the command line on your host to verify you've got things working correctly! The stuff in &lt;&gt; are the keys to hit at the end of the line...</p> <p><code>$ mail you@email.domain&lt;ENTER&gt;</code></p> <p><code>Subject: Testing from your.relay.server.domain&lt;ENTER&gt;<br /> Testing postfix remote host&lt;ENTER&gt;<br /> &lt;CTRL-D&gt;<br /> Cc:&lt;ENTER&gt;</code></p> <p>Typing &lt;CTRL-D&gt; (hold down the Control or Ctrl key on your keyboard and press the "d" key) will finish your message, showing you a "CC:" field, in which you can type in other email addresses if you want to test sending to multiple addresses. When you then hit &lt;ENTER&gt;, it will attempt to send this email. It might take a few minutes to work its way through to the receiving email system (having to run the gauntlet of spam and virus filters on the way).</p> <p>You can also always check the postfix system logs to see what postfix thinks about it using the command above. Hit &lt;SHIFT-F&gt; to have the log update in real time.</p> <h2>Done</h2> <p>Now you've got working outgoing email from your server. That means many higher-level web applications you might install on your infrastructure will work out-of-the-box, because what you've set up, for example, enables the default PHP email service and that used by other stacks.</p> <h2>Sending from Docker Containers</h2> <p>You can configure your server so you can reference it from services you run from Docker containers on your host. You do this by referencing the host, like via an ad hoc SMTP server on your container like <a href="https://marlam.de/msmtp/">msmtp</a>, and you can just reference it as 172.17.0.1, which is the default base IP for Docker hosts from the perspective of Docker containers. You might find it's different on your particular install. In that case, you have to make your Postfix SmartHost accept email for sending from the Docker containers on that server. There're quite a few examples of that among <a href="https://git.oeru.org/explore/projects?utf8=%E2%9C%93&amp;name=docker&amp;sort=latest_activity_desc">my Docker recipes on the OERu's git repository</a>.</p> </div> </div> </div> <section class="field field-node--field-blog-comments field-name-field-blog-comments field-type-comment field-label-above comment-wrapper"> <a name="comments"></a> <div class="comment-form-wrapper"> <h2 class="comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=28&amp;2=field_blog_comments&amp;3=comment" token="lLRkGAi5P6j9iM99_jKG1YxvJLITyB02GoT7oM-A7oA"></drupal-render-placeholder> </div> </section> Fri, 20 Sep 2019 02:08:28 +0000 dave 28 at http://tech.oeru.org http://tech.oeru.org/configuring-linux-server-send-email-postfix-smtp-server-using-external-authenticating-smtp-host#comments Hourly versioned MongoDB backup http://tech.oeru.org/hourly-versioned-mongodb-backup <span class="field field--name-title field--type-string field--label-hidden">Hourly versioned MongoDB backup</span> <div class="field field-node--field-blog-tags field-name-field-blog-tags field-type-entity-reference field-label-above"> <h3 class="field__label">Blog tags</h3> <div class="field__items"> <div class="field__item field__item--mongodb"> <span class="field__item-wrapper"><a href="/taxonomy/term/14" hreflang="en">mongodb</a></span> </div> <div class="field__item field__item--backup"> <span class="field__item-wrapper"><a href="/taxonomy/term/57" hreflang="en">backup</a></span> </div> <div class="field__item field__item--docker"> <span class="field__item-wrapper"><a href="/taxonomy/term/16" hreflang="en">docker</a></span> </div> <div class="field__item field__item--docker-compose"> <span class="field__item-wrapper"><a href="/taxonomy/term/49" hreflang="en">docker-compose</a></span> </div> <div class="field__item field__item--bash"> <span class="field__item-wrapper"><a href="/taxonomy/term/58" hreflang="en">bash</a></span> </div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">dave</span></span> <span class="field field--name-created field--type-created field--label-hidden">Mon 29/04/2019 - 16:28</span> <div class="clearfix text-formatted field field-node--body field-name-body field-type-text-with-summary field-label-hidden"> <div class="field__items"> <div class="field__item"><p>Because the collaboration of an open community is its real history, I place a high value on backing up the Rocket.Chat servers I'm responsible for, and <em>especially</em> the data they generate, held in MongoDB files (on the host) managed by a MongoDB container.</p> <p>To do that reliably, I have set up <a href="https://git.oeru.org/oeru/mongobackup">a bash script</a> which does an hourly backup of all MongoDB "databases" and automatically maintains 24 hourly, 7 daily, 4 weekly, 12 monthly, and 7 yearly snapshots of the databases.</p> <p>Once you've configured your backup script properly, you should be able to run this command to do a backup...</p> <p><code>/etc/mongobackup/dbbackup-mongo --hourly</code></p> <p>easy-peasy.</p> </div> </div> </div> <section class="field field-node--field-blog-comments field-name-field-blog-comments field-type-comment field-label-above comment-wrapper"> <a name="comments"></a> <div class="comment-form-wrapper"> <h2 class="comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=27&amp;2=field_blog_comments&amp;3=comment" token="a3hwmZDJbiAJ569rEIXJYUfTF3BaDQbUaOh1gRHfc5g"></drupal-render-placeholder> </div> </section> Mon, 29 Apr 2019 04:28:43 +0000 dave 27 at http://tech.oeru.org http://tech.oeru.org/hourly-versioned-mongodb-backup#comments Upgrading RocketChat to 1.0.x and MongoDB to 4.0 http://tech.oeru.org/upgrading-rocketchat-10x-and-mongodb-40 <span class="field field--name-title field--type-string field--label-hidden">Upgrading RocketChat to 1.0.x and MongoDB to 4.0</span> <div class="field field-node--field-blog-tags field-name-field-blog-tags field-type-entity-reference field-label-above"> <h3 class="field__label">Blog tags</h3> <div class="field__items"> <div class="field__item field__item--rocketchat"> <span class="field__item-wrapper"><a href="/taxonomy/term/18" hreflang="en">rocket.chat</a></span> </div> <div class="field__item field__item--mongodb"> <span class="field__item-wrapper"><a href="/taxonomy/term/14" hreflang="en">mongodb</a></span> </div> <div class="field__item field__item--docker"> <span class="field__item-wrapper"><a href="/taxonomy/term/16" hreflang="en">docker</a></span> </div> <div class="field__item field__item--docker-compose"> <span class="field__item-wrapper"><a href="/taxonomy/term/49" hreflang="en">docker-compose</a></span> </div> <div class="field__item field__item--ubuntu-linux"> <span class="field__item-wrapper"><a href="/taxonomy/term/12" hreflang="en">ubuntu linux</a></span> </div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">dave</span></span> <span class="field field--name-created field--type-created field--label-hidden">Mon 29/04/2019 - 14:39</span> <div class="clearfix text-formatted field field-node--body field-name-body field-type-text-with-summary field-label-hidden"> <div class="field__items"> <div class="field__item"><p>With the recent release of Rocket.Chat 1.0.x (after a couple years undergoing development at a fairly blistering pace), it's time for many of us to upgrade!</p> <p>Previously, I showed how to <a href="/docker-compose-better-way-deploy-rocketchat-wekan-and-mongodb">install Rocket.Chat via Docker Compose</a> but that was a much earlier version of Rocket.Chat and version 3.4 of MongoDB, which is now quite old (by FOSS standards at least). And it turns out upgrading everything has a few gotchas, so here's how I managed to do it.</p> <p>Before you do <em>anything</em> <a href="/hourly-versioned-mongodb-backup">do a backup of your MongoDB</a>!</p> <p>The first thing you need to do is upgrade <em>the way</em> in which you're running MongoDB. You have to enable a capability called "Local Replication".</p> <h2>Update your Docker Compose configuration</h2> <p>My first step, after logging into my virtual machine via SSH as the <em>unprivileged user</em> that I created to run docker commands, was to update my <code>docker-compose.yml</code> file (if you followed my previous instructions, you'll find it in <code>/home/www/docker-rocketchat-wekan-mongo</code>). </p> <p>First, make a backup of it nearby...</p> <p>cd <code>/home/www/docker-rocketchat-wekan-mongo</code><br /> cp docker-compose.yml docker-compose.yml-mongo3.4</p> <p>and then edit the file to say this:</p> <p><code>version: '2'<br /> services:<br />   mongo:<br />     restart: unless-stopped<br />     image: mongo<strong>:3.4</strong><br />     volumes:<br />       - [data directory path]:/data/db<br />       - [backup directory path]:/backups<br />     command: --smallfiles <strong>--oplogSize 128 --replSet rs0</strong><br /><strong>  # this container's job is just run the command to initialize the replica set.<br />   # it will run the command and remove himself (it will not stay running)<br />   mongo-init-replica:<br />     image: mongo:3.4<br />     command: 'bash -c "for i in `seq 1 30`; do mongo mongo/rocketchat --eval \"rs.initiate({ _id: ''rs0'', members: [ { _id: 0, host: ''localhost:27017'' } ]})\" &amp;&amp; s=$$? &amp;&amp; break || s=$$?; echo \"Tried $$i times. Waiting 5 secs...\"; sleep 5; done; (exit $$s)"'<br />     depends_on:<br />       - mongo</strong><br />   rocketchat:<br />     restart: unless-stopped<br />     image: rocketchat/rocket.chat<strong>:latest</strong></code><br /><code><strong>    command: bash -c 'for i in `seq 1 30`; do node main.js &amp;&amp; s=$$? &amp;&amp; break || s=$$?; echo "Tried $$i times. Waiting 5 secs..."; sleep 5; done; (exit $$s)'</strong><br />     ports:<br />       - "127.0.0.1:[port number]:3000" # should be a free port above 1024<br />     depends_on:<br />       - mongo<br />     environment:<br />       - MONGO_URL=mongodb://mongo/rocket<br /><strong>      - MONGO_OPLOG_URL=mongodb://mongo/local</strong><br />       - ROOT_URL=[domain name (including schema, e.g. http://)]<br />     volumes:<br />       - [upload directory path]:/var/www/rocket.chat/uploads</code><br /><strong><code>    labels:<br />       - "traefik.backend=rocketchat"<br />       - "traefik.frontend.rule=Host: [your domain name (<em>not </em>including schema)]"</code></strong></p> <p>Now, having updated your docker-compose.yml file, you have to do a couple other things. To do the upgrade from MongoDB 3.4 to 4.0, you have to do the interim upgrade to 3.6 first.</p> <h2>Enabling Local Replication</h2> <p>First you need to check what version of MongoDB you're <em>currently</em> using - both the version you're running <em>and</em> the "Feature Compatibility Version" (you can run a newer version of MongoDB, but configure it to only run features from some previous version to avoid breaking older software that depends on old features)... Do this as follows.</p> <p>Access your MongoDB instance:</p> <p><code>docker-compose exec mongo bash</code></p> <p>That should give you a command prompt that looks like this:</p> <p><code>root@a56eefe9f352: # </code></p> <p>but the container identifier (after the @) will be different (but the same length). At that prompt, you can run this command:</p> <p><code>mongo --eval "db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )"</code></p> <p>Tip: if you're on a Linux desktop, you can copy this command (via CTRL-C) from this document and past it into your SSH terminal window (via CTRL+SHIFT-V).</p> <p>It should tell you you're either running "featureCompatibilityVersion" 3.2 or 3.4. If it's the latter, skip this next step. If not, run this next:</p> <p><code>mongo --eval "db.adminCommand( { setFeatureCompatibilityVersion: '3.4' } )"</code></p> <p>to set the version to 3.4. If the command succeeds you'll likely see something like</p> <p><code>{ "ok" : 1 }</code></p> <p>as the response.</p> <p>Now you can upgrade your Mongo 3.4 is the latest version (should be 3.4.20 at the time of this writing). Get out of the container (back to your Docker host) via CTRL-D (or "exit" - they're synonymous for logging out of a terminal session). Then you can run:</p> <p><code>docker-compose pull mongo</code></p> <p>That should update both your Mongo docker container to the latest version in the 3.4 series.</p> <p>The final step to enabling local replication is to run</p> <p><code>docker-compose up -d mongo mongo-init-replica &amp;&amp; docker-compose logs -f</code></p> <p>That will restart MongoDB and drop you into the stream of logging from all the containers (including the rocket.chat container). It'll also start the "mongo-init-replica" container.  That container should run briefly <em>and then exit cleanly</em> having set up the local replication that you'll need for subsequent upgrades to MongoDB!</p> <p>Check for any errors in the output... there might be a couple if it takes your MongoDB a bit of time to accept connections... as long as it eventually stops showing errors, you should be ok!</p> <p> </p> <h2>Upgrading Rocket.Chat to 1.0.x</h2> <p>Now that you're fully on version 3.4, running in local replica mode, you can update your Rocket.Chat instance.   Rocket.Chat still supports Mongo 3.4 (it won't for long, thus this tutorial!), so you can now upgrade the Rocket.Chat container as well as make sure your Mongo 3.4 is the latest version (should be 3.4.20 at the time of this writing).</p> <p>Note that the latest version of the Rocket.Chat docker container could be quite a lot higher when you read this... if it's beyond, say, 1.1 it might be unsafe to use the approach I'm describing. You can <a href="https://github.com/RocketChat/Rocket.Chat/releases">check the current version release status</a>. To protect yourself, you can alter the rocket.chat image line in your docker-compose.yml file to explicitly tell it to use the 1.0.x series for which these instructions should continue to apply... pick the highest 1.0.x version you can find in the releases and alter the line in docker-compose.yml to specify that version:</p> <p><code>    image: rocketchat/rocket.chat<strong>:1.0.1</strong></code></p> <p>or whatever the latest 1.0.x version is. Then you can run:</p> <p><code>docker-compose pull</code> rocketchat</p> <p>which will update your Rocket.Chat from the current version to the one specified.</p> <p>Then restart your Rocket.Chat instance:</p> <p><code>docker-compose up -d &amp;&amp; docker-compose logs -f</code></p> <p>That should restart both MongoDB and Rocket.Chat, and drop you into the stream of logging from both containers. It'll also start the "mongo-init-replica" container again, but having done its job it should exit happily again.</p> <p>Check for any errors in the output... there might be a couple if it takes your MongoDB a bit of time to accept connections... as long as it eventually stops showing errors, you should be ok! Eventually, you should see something similar to (with version details updated appropriately):</p> <p> </p> <p><code>+----------------------------------------------+<br /> |                SERVER RUNNING                |<br /> +----------------------------------------------+<br /> |                                              |<br /> |  Rocket.Chat Version: 1.0.1                  |<br /> |       NodeJS Version: 8.11.4 - x64           |<br /> |      MongoDB Version: 3.4.20                 |<br /> |       MongoDB Engine: wiredTiger             |<br /> |             Platform: linux                  |<br /> |         Process Port: 3000                   |<br /> |             Site URL: https://chat.oeru.org  |<br /> |     ReplicaSet OpLog: Enabled                |<br /> |          Commit Hash: 60f1a4afd6             |<br /> |        Commit Branch: HEAD                   |<br /> |                                              |<br /> +----------------------------------------------+</code></p> <p>Your instance is now running the right version! Time to tidy things up by upgrading Mongo the rest of the way to 4.0!</p> <p> </p> <h2>Upgrading to MongoDB 3.6</h2> <p>Now you can upgrade Mongo to 3.6. First, adjust your docker-compose.yml file.  Update both occurances of this line:</p> <p><code>image: mongo:3.4</code></p> <p>to</p> <p><code>image: mongo:3.6</code></p> <p>Then you can do another</p> <p><code>docker-compose pull mongo</code></p> <p>which will download the newer Mongo 3.6 docker container. Then you can again run</p> <p><code>docker-compose up -d &amp;&amp; docker-compose logs -f</code></p> <p>Again check for errors. If there are none (other than perhaps a brief set of "mongo is not accepting connections" errors), you should be fine to update the "compatibility version" from 3.4 to 3.6... Get a session on your Mongo container via</p> <p><code>docker-compose exec mongo bash</code></p> <p>and then (as above) run this:</p> <p><code>mongo --eval "db.adminCommand( { setFeatureCompatibilityVersion: '3.6' } )"</code></p> <p>which should give you a more complicated response than that for the 3.4 transition, but it should still more or less say "ok"... To make sure everything's happy with the change, it's wise to run</p> <p><code>docker-compose up -d &amp;&amp; docker-compose logs -f</code></p> <p>again and make sure there're no obvious errors. If not (after making another database backup for safety!!) we can proceed to Mongo 4.0!</p> <h2>Final push to MongoDB 4.0</h2> <p>Finally, you can again edit your docker-compose.yml and change both occurrences of</p> <p> </p> <p><code>image: mongo:3.6</code></p> <p>to</p> <p><code>image: mongo:4.0</code></p> <p>Then you can do a final</p> <p><code>docker-compose pull mongo</code></p> <p>which will download the newer Mongo 4.0 docker container. Then you can again run</p> <p><code>docker-compose up -d &amp;&amp; docker-compose logs -f</code></p> <p>And, assuming you don't see any errors, you can push the feature compatibility to 4.0:</p> <p><code>docker-compose exec mongo bash</code></p> <p>and then run:</p> <p><code>mongo --eval "db.adminCommand( { setFeatureCompatibilityVersion: '4.0' } )"</code></p> <p>followed by a final</p> <p><code>docker-compose up -d &amp;&amp; docker-compose logs -f</code></p> <p>And, again, if you don't see any errors... you should get something a bit like this:</p> <p><code>+----------------------------------------------+<br /> |                SERVER RUNNING                |<br /> +----------------------------------------------+<br /> |                                              |<br /> |  Rocket.Chat Version: 1.0.1                  |<br /> |       NodeJS Version: 8.11.4 - x64           |<br /> |      MongoDB Version: 4.0.9                  |<br /> |       MongoDB Engine: wiredTiger             |<br /> |             Platform: linux                  |<br /> |         Process Port: 3000                   |<br /> |             Site URL: https://chat.oeru.org  |<br /> |     ReplicaSet OpLog: Enabled                |<br /> |          Commit Hash: 60f1a4afd6             |<br /> |        Commit Branch: HEAD                   |<br /> |                                              |<br /> +----------------------------------------------+</code></p> <p>you're done and future proofed!</p> </div> </div> </div> <section class="field field-node--field-blog-comments field-name-field-blog-comments field-type-comment field-label-above comment-wrapper"> <a name="comments"></a> <div class="comment-form-wrapper"> <h2 class="comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=26&amp;2=field_blog_comments&amp;3=comment" token="VK6tqiyjVB7HYLx9xMKgF1KeXWiFxiQ-XNY71DIaaF0"></drupal-render-placeholder> </div> </section> Mon, 29 Apr 2019 02:39:33 +0000 dave 26 at http://tech.oeru.org http://tech.oeru.org/upgrading-rocketchat-10x-and-mongodb-40#comments Setting up your own BitWarden password keeper and sync server http://tech.oeru.org/setting-your-own-bitwarden-password-keeper-and-sync-server <span class="field field--name-title field--type-string field--label-hidden">Setting up your own BitWarden password keeper and sync server</span> <div class="field field-node--field-blog-tags field-name-field-blog-tags field-type-entity-reference field-label-above"> <h3 class="field__label">Blog tags</h3> <div class="field__items"> <div class="field__item field__item--docker-compose"> <span class="field__item-wrapper"><a href="/taxonomy/term/49" hreflang="en">docker-compose</a></span> </div> <div class="field__item field__item--docker"> <span class="field__item-wrapper"><a href="/taxonomy/term/16" hreflang="en">docker</a></span> </div> <div class="field__item field__item--rust"> <span class="field__item-wrapper"><a href="/taxonomy/term/59" hreflang="en">rust</a></span> </div> <div class="field__item field__item--bitwarden"> <span class="field__item-wrapper"><a href="/taxonomy/term/60" hreflang="en">BitWarden</a></span> </div> <div class="field__item field__item--password-keeper"> <span class="field__item-wrapper"><a href="/taxonomy/term/61" hreflang="en">password keeper</a></span> </div> <div class="field__item field__item--privacy"> <span class="field__item-wrapper"><a href="/taxonomy/term/62" hreflang="en">privacy</a></span> </div> <div class="field__item field__item--security"> <span class="field__item-wrapper"><a href="/taxonomy/term/63" hreflang="en">security</a></span> </div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">dave</span></span> <span class="field field--name-created field--type-created field--label-hidden">Wed 23/01/2019 - 11:30</span> <div class="field field-node--field-image field-name-field-image field-type-image field-label-hidden has-multiple"> <figure class="field-type-image__figure image-count-1"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2019-04/bitwarden_desktop_app.png?itok=9ZE2TxZh" title="BitWarden desktop app (Electron-based)" data-colorbox-gallery="gallery-field_image-ewTrdr0Bi88" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;BitWarden desktop app (Electron-based)&quot;}"><img src="/sites/default/files/styles/medium/public/2019-04/bitwarden_desktop_app.png?itok=hekOd__9" width="220" height="169" alt="BitWarden desktop app (Electron-based)" typeof="foaf:Image" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-2"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2019-04/bitwarden_web_interface_0.png?itok=QShOqyOM" title="BitWarden website interface" data-colorbox-gallery="gallery-field_image-ewTrdr0Bi88" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;BitWarden website interface&quot;}"><img src="/sites/default/files/styles/medium/public/2019-04/bitwarden_web_interface_0.png?itok=QitHloR0" width="220" height="158" alt="BitWarden website interface" typeof="foaf:Image" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-3"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2019-04/bitwarden_desktop_app_logged_in_0.png?itok=FAbPSjim" title="Desktop BitWarden app, with user logged in. " data-colorbox-gallery="gallery-field_image-ewTrdr0Bi88" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Desktop BitWarden app, with user logged in. &quot;}"><img src="/sites/default/files/styles/medium/public/2019-04/bitwarden_desktop_app_logged_in_0.png?itok=p9Oarp8h" width="220" height="156" alt="Desktop BitWarden app, with user logged in. " typeof="foaf:Image" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-4"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2019-04/bitwarden_adding_new_item_via_web_interface_0.png?itok=_PTdZqjB" title="Web interface for BitWarden with user logged in." data-colorbox-gallery="gallery-field_image-ewTrdr0Bi88" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Web interface for BitWarden with user logged in.&quot;}"><img src="/sites/default/files/styles/medium/public/2019-04/bitwarden_adding_new_item_via_web_interface_0.png?itok=YQftj3vf" width="220" height="209" alt="Web interface for BitWarden with user logged in." typeof="foaf:Image" class="image-style-medium" /> </a> </div> </figure> </div> <div class="clearfix text-formatted field field-node--body field-name-body field-type-text-with-summary field-label-hidden"> <div class="field__items"> <div class="field__item"><p>One of the key requirements of pursuing Good Digital Hygiene is <em>using strong passwords</em>, and a <em>different strong password for every application</em>. This is relatively easy to do in theory, <em>with the aid of clever software</em>, but it's something desperately few people do well in practice. I'm going to explain how I've addressed this issue of digital hygiene for myself, and how you can do it for yourself, <em>and your entire family, social circle, or community</em>.</p> <p>Password Keepers (or managers or safes) have emerged as that "clever software". A good password keeper has to do a bunch of things to be really useful:</p> <ol><li>It needs to store your passwords somewhere in an encrypted form (so if someone gets your password database, they can't work out your entire collection of passwords). You only need to remember <em>one </em><em>really strong password/phrase to unlock all of them. </em></li> <li>It needs to work in whatever context you need a password. Like <ol><li>your desktop/laptop, where you need to remember logins for a variety of apps and services,</li> <li>in your browser (for web apps that require authentication), and</li> <li>on your mobile platforms (because most services you use via apps or browsers require authentication)</li> </ol></li> <li>It needs to be cross platform <ol><li>must support Windows, MacOS, and Linux OSs,</li> <li>must support extensions for many browsers like Firefox, Chrome/<a href="https://chromium.org" title="The open source project that underlies Chrome and is 99% identical.">Chromium</a>, Safari, and others, and</li> <li>must support mobile OSs like iOS and Android.</li> </ol></li> <li>It needs to sync data in a timely manner among all the different contexts in which a given user needs it.</li> </ol><p>That's a lot of requirements. There're quite a few efforts that have had a crack at solving this. The <a href="https://www.keepassx.org/">KeePassX</a> community has been addressing this for ages and has created a comprehensive (if variable) ecosystem of apps which work across all of the required platforms, but only with a lot of work.</p> <p>In the proprietary world, there're many options, with a few front runners like 1Password and <a href="https://lastpass.com">LastPass</a>. The former doesn't work on Linux, so it only gets a passing reference and no link :) (update 2019-05-31 - <a href="https://1password.com">1Password</a> has added Linux support). The latter, which I used (grudgingly, mostly because I couldn't get KeePassX to work for me) for a few years, works across all the platforms relevant to me, but it was becoming progressively more invasive and annoying to use. Also, because it has a <em>lot</em> of users, and stores everything (albeit, encrypted) in a centralised cloud repository, it's a <em>big target</em>. Also, with its largely proprietary code, I wasn't happy trusting it. </p> <p>Then I heard about <a href="https://bitwarden.com">BitWarden</a>. They offered a commercial service (with a free tier) that I could quickly try... they supported all the OSs, mobile and desktop, and browsers that I use... <em>and they release their entire codebase </em>(server and clients) <em><strong>under open source licenses.</strong></em> I tried it, it worked for me, I was sold!</p> <p>Then I decided I wanted to run my own BitWarden server, rather than use their commercial centralised cloud platform (because, as with LastPass, it's a tempting target). That's when I found out the server of BitWarden was written using Microsoft technologies, C# (yeah, it's mostly open source, but it's dirty to me due to its Microsoft legacy), and MS SQL Server, which is a nasty proprietary dependency (especially given how basic the database requirements for this sort of application are).</p> <p>So I was devastated that I couldn't set up my own server without compromising my iron-clad anti-Microsoft position (I've managed to maintain it for the past 25 years)... until another Free and Open Source Software aficionado pointed me at Daniel Garcia's work! Daniel has implemented a <a href="https://github.com/dani-garcia/bitwarden_rs">full (unofficial) BitWarden work-alike using a fully FOSS stack</a>: the Rust language, storing data in SQLite, and (quite thoughtfully) re-using other open source licensed components of the BitWarden system that don't have proprietary dependencies, including the website code and layout (which is part of the server).</p> <p>Daniel's server implementation also unlocks all the "premium" services that BitWarden offers through their hosted service, too... so that's a nice bonus.</p> <p>Another open source developer, <a href="https://github.com/mprasil">mpasil</a>, has created <a href="https://github.com/mprasil/bitwarden_rs">a "fork" of Daniel's project</a> from which he maintains an up-to-date Docker container on hub.docker.com. <strong>Thanks to both Daniel Garcia and mpasil's efforts</strong>, it turns out to be quite straightforward to set up your own Docker-based BitWarden-compatible service! Here's how...</p> <h2>Creating your own BitWarden Service</h2> <h3>Set up a Virtual Server</h3> <p>The first step is to get yourself an entry-level virtual server or compute instance somewhere. I generally use DigitalOcean (I have no affiliation with the company), but there are many other commodity hosting services (check out Vultr or Linode, for example) around the world which offer comparably (or better) spec'd servers for <strong>USD5.00/month</strong>, or <strong>USD60.00/year</strong> - I encourage you to do a bit of research. For that you get a Gigabyte (GB) of RAM, a processor, and 40GB of SSD (Static Storage Device = faster) storage. That's <em>oodles</em> of grunt for what this application requires.</p> <p>I suggest you create an account for yourself (and I encourage you to use Two Factor Authentication, aka 2FA) and create an Ubuntu 18.04 (or the most recent LTS version - the next will be 20.04, in April 2020 :) ) in the zone nearest to you. You'll need to note the server's IP address (it'll be a series of 4 numbers, 0-254, separated by full stops, e.g. 103.99.72.244). With that, you can <a href="https://www.digitalocean.com/community/tutorials/how-to-use-ssh-to-connect-to-a-remote-server-in-ubuntu">log into it via SSH</a>.</p> <h3>Get your Domain lined up</h3> <p>You will want to have a domain to point at your server, so you don't have to remember the IP number. There're are thousands of domain "registrars" in the world who'll help you do that... You just need to "register" a name, and you pay yearly fee (usually between USD10-30 depending on the country and the "TLD" (Top Level Domain. There're national ones like .nz, .au, .uk, .tv, .sa, .za, etc., or international domains (mostly associated with the US) like .com, .org, .net, and a myriad of others. Countries decide on how much their domains wholesale for and registrars add a margin for the registration service).</p> <p>Here in NZ, I use the services of <a href="https://metaname.net">Metaname</a> (they're local to me in Christchurch, and I know them personally and trust their technical capabilities). If you're not sure who to use, ask your friends. Someone's bound to have recommendations (either positive or negative, in which case you'll know who to avoid).</p> <p>If you want to use your domain for other things besides your BitWarden instance, I'd encourage you to use a <em>subdomain</em>, like (my usual choice) is "safe.domainname", namely the subdomain "safe" of "domainname".</p> <p>Once you have selected and registered your domain, you can set up (usually through a web interface provided by the registrar) an "A Record" which associates your website's name to the IP address of your server. So you should just be able to enter your server's IP address, the domain name (or sub-domain) you want to use for your BitWarden service, and that's it. For a password safe, I tend to use the subdomain "safe", so, for example, safe.mydomain.nz or similar.</p> <p>You might be asked to set a "Time-to-live" (which has to do with the length of time Domain Name Servers are asked to "cache" the association that the A Record specifies) in which case you can put in 3600 seconds or an hour depending on the time units your interface requests... but in most cases that'll be set to a default of an hour automatically.</p> <p>You should be able to test that your A Record has been set correctly by SSHing to your domain name rather than the IP address. It should (after you accept the SSH warning that the server's name has changed) work the same way your original SSH login did.</p> <h3>Set up a Docker Server</h3> <p>Once I've first logged into it as the "root" (full admin) user, here's what I usually do:</p> <ol><li>I create an "unprivileged user", either with my name "dave" or sometimes an "ubuntu" user (some hosting providers create a default unprivileged user of "ubuntu" when you create an Ubuntu-based virtual machine. Some create a "debian" user for Debian-based VMs, etc.) via<br /><code>adduser ubuntu</code></li> <li>I install a few core applications: my preferred editor <a href="https://en.wikipedia.org/wiki/Vim_(text_editor)">vim</a> (<a href="https://en.wikipedia.org/wiki/GNU_nano">nano</a> is another easy option and comes pre-installed on Ubuntu), version control system, <a href="https://en.wikipedia.org/wiki/Git">git</a>, and a very handy configuration tracker, <a href="http://joeyh.name/code/etckeeper/">etckeeper</a>:<br /><code>apt-get update &amp;&amp; apt-get install vim git etckeeper</code></li> <li>I do some basic configuration of git (replace the [tokens] with the real values for you, minus the []):<br /><code>git config --global user.email "[your email]"<br /> git config --global user.name "[your full name, e.g. Jane Doe]"</code></li> <li>Initialise etckeeper - it will track configuration changes you make to your system which can be invaluable in replicating a server or working out what's changed if something breaks.<br /><code>etckeeper init<br /> etckeeper commit -m "initial commit of BitWarden host"</code></li> <li>Install Docker dependencies:<br /><code>apt-get install apt-transport-https ca-certificates curl software-properties-common pwgen</code><br /> Install secure key needed to add the docker.com package repository to your system<br /><code>curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -</code><br /> Confirm the key is valid<br /><code>apt-key fingerprint 0EBFCD88</code><br /> (you should see something like "<code>uid [ unknown] Docker Release (CE deb) &lt;docker@docker.com&gt;</code>" among the 4 lines)</li> <li>Add the repository for your Ubuntu version (this will pick it automatically)<br /><code>add-apt-repository    "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"</code></li> <li>Update the package repository to include the packages from docker.com<br /><code>apt-get update</code></li> <li>Install the Community Edition of the Docker service<br /><code>apt-get install docker-ce</code></li> <li>Add your unprivileged user ("ubuntu" in this case - substitute the unprivileged user you created!) to a new "docker" group and add that user to other useful groups:<br /><code>groupadd docker<br /> adduser ubuntu<br /> adduser ubuntu sudoers<br /> adduser ubuntu admin</code><br /><code>adduser ubuntu docker</code></li> <li>Create an SSH key for your unprivileged user and allow logins for that user from external connection:<br /><code>sudo -Hu ubuntu ssh-keygen -t rsa<br /> cp /root/.ssh/authorized_keys /home/ubuntu/.ssh/<br /> chown ubuntu:ubuntu /home/ubuntu/.ssh/<br /> adduser ubuntu ssh</code></li> <li>Install the Python packaging system, "pip" to allow you to install and maintain the Docker Compose framework for managing collections of Docker containers:<br /><code>apt install python-pip<br /> pip install -U pip<br /> pip install docker-compose</code></li> <li>Set a convenience variable for [your domain] here (note: it'll only be recognised for this session, i.e. until you log out):<br /><code>DOMAIN=[your domain]<br /> USER=[unprivileged user, e.g. ubuntu]</code><br /> Below, anytime you see $DOMAIN in a command, it'll be replaced by whatever you put in for [your domain] and similarly $USER...</li> <li>Create directories to hold both the Docker Compose configurations and the persistent data you don't want to lose if you remove your Docker containers (namely your password database and configuration information):<br /><code>mkdir -p /home/docker/$DOMAIN &amp;&amp; mkdir -p /home/data/$DOMAIN<br /> chown -R ${USER}:${USER} /home/data /home/docker/</code></li> <li>Install the NGINX (pronounced "Engine X") webserver which will act as a reverse proxy for the BitWarden service and terminate the encryption via HTTPS:<br /><code>apt-get install nginx-full</code></li> <li>Configure the server's firewill and make an exception for SSH and NGINX services<br /><code>ufw allow OpenSSH<br /> ufw allow "Nginx Full"<br /> ufw enable</code><br /> Check that its running via<br /><code>ufw status</code></li> <li>Create a directory for including files for NGINX<br /><code>cd /etc/nginx</code><br /><code>mkdir includes</code><br /> Choose your text editor for editing files. Here're options for Vim or Nano - you can install and select others. Setting the EDIT shall variable allows you to copy and paste these commands regardless of which editor you prefer as it'll replace the value of $EDIT with the full path to your preferred editor.<br /><code>EDIT=`which nano`</code> or <code>EDIT=`which vim`</code></li> <li>To support encrypted data transfer between external devices and your server using HTTPS,  you need a valid SSL certificate. Until recently, these were costly and hard to get. With <a href="/protecting-your-users-lets-encrypt-ssl-certs">Let's Encrypt</a>, they've become a straightforward and essential part of any good (user-respecting) web site or service. To facilitate getting and periodically renewing your SSL certificate, you need to create the file letsencrypt.conf:<br /><code>$EDIT includes/letsencrypt.conf</code><br /> and enter the following content: <p><blockcode><code>#############################################################################<br /> # Configuration file for Let's Encrypt ACME Challenge location<br /> # This file is already included in listen_xxx.conf files.<br /> # Do NOT include it separately!<br /> #############################################################################<br /> #<br /> # This config enables to access /.well-known/acme-challenge/xxxxxxxxxxx<br /> # on all our sites (HTTP), including all subdomains.<br /> # This is required by ACME Challenge (webroot authentication).<br /> # You can check that this location is working by placing ping.txt here:<br /> # /var/www/letsencrypt/.well-known/acme-challenge/ping.txt<br /> # And pointing your browser to:<br /> # http://xxx.domain.tld/.well-known/acme-challenge/ping.txt<br /> #<br /> # Sources:<br /> # https://community.letsencrypt.org/t/howto-easy-cert-generation-and-renewal-with-nginx/3491<br /> #<br /> # Rule for legitimate ACME Challenge requests<br /> location ^~ /.well-known/acme-challenge/ {<br />     default_type "text/plain";<br />     # this can be any directory, but this name keeps it clear<br />     root /var/www/letsencrypt;<br /> }<br /> # Hide /acme-challenge subdirectory and return 404 on all requests.<br /> # It is somewhat more secure than letting Nginx return 403.<br /> # Ending slash is important!<br /> location = /.well-known/acme-challenge/ {<br />     return 404;<br /> }</code></blockcode></p> </li> <li> <p>Now you need to create the directory described in the letsencrypt.conf file:<br /><code>mkdir /var/www/letsencrypt</code></p> </li> <li> <p>Create "<a href="https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html#Forward_Secrecy_&amp;_Diffie_Hellman_Ephemeral_Parameters">forward secrecy &amp; Diffie Hellman ephemeral parameters</a>" to make your server more secure... The result will be a secure signing key stored in <code>/etc/ssl/certs/dhparam.pem</code> (note, getting enough "entropy" to generate sufficient randomness to calculate this will take a few minutes!<code>):</code><br /><code>openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096</code></p> </li> <li> <p>and then you need to create the reverse proxy configuration file as follows:<br /><code>cd ../sites-available</code><br /><br /> and fill it with this content, replacing all [tokens] with your relevant values:<br /><blockcode><code>#<br /> # HTTP does *soft* redirect to HTTPS<br /> #<br /> server {<br />     # add [IP-Address:]80 in the next line if you want to limit this to a single interface<br />     listen 0.0.0.0:80;<br />     </code></blockcode><blockcode><code>server_name [your domain];<br />     root /home/data/[your domain];<br />     index index.php;<br /><br />     # change the file name of these logs to include your server name<br />     # if hosting many services...<br />     access_log /var/log/nginx/[your domain]_access.log;<br />     error_log /var/log/nginx/[your domain]_error.log;  <br />     include includes/letsencrypt.conf;</code></blockcode><blockcode><br /><br /><code>    # redirect all HTTP traffic to HTTPS.<br />     location / {<br />         return  302 https://[your domain]$request_uri;<br />     }<br /> }</code></blockcode><br /> and make the configuration available to NGINX by linking the file from sites-available into sites-enabled (you can disable the site by removing the link and reloading NGINX)<br /><code>ln -sf sites-available/bitwarden sites-enabled/bitwarden</code><br /> Check to make sure NGINX is happy with the configuration<br /><code>nginx -t </code><br /> If you don't get any errors, you can restart NGINX<br /><code>service nginx restart</code><br /> and it should be configured properly to respond to requests at <code>http://[your domain]/.well-known/acme-challenge/ </code>which is required for creating a Let's Encrypt certificate.<br /><code>$EDIT sites-available/bitwarden</code></p> </li> <li> <p>So now we can create the certificate. You'll need to install the letscencrypt scripts:<br /><code>apt-get install letsencrypt</code><br /> You will be asked to enter some information about yourself, including an email address - this is necessary so that the letsencrypt service can email you if any of your certificates are not successfully updated (they need to be renewed every few weeks - normally this happens automatically!) so that you site and users aren't affected by an expired SSL certificate (a bad look!). Trust me, these folks are the good guys.<br /> You create a certificate for [your domain] with the following command (with relevant substitutions):<br /><code>letsencrypt certonly --webroot -w /var/www/letsencrypt -d $DOMAIN</code><br /> If the process works, you should see a "Congratulations!" message.</p> </li> <li> <p>Edit the nginx configuration file for the BitWarden service again<br /><code>$EDIT sites-available/bitwarden</code><br /> and add the following to the bottom of <code>file (starting the line below the final "}")<br /><blockcode>#<br /> # HTTPS<br /> #<br /> # This assumes you're using Let's Encrypt for your SSL certs (and why wouldn't<br /> # you!?)... https://letsencrypt.org<br /> server {<br />     # add [IP-Address:]443 ssl in the next line if you want to limit this to a single interface<br />     listen 0.0.0.0:443 ssl;<br />     ssl on;<br />     ssl_certificate /etc/letsencrypt/live/[your domain]/fullchain.pem;<br />     ssl_certificate_key /etc/letsencrypt/live/[your domain]/privkey.pem;<br />     ssl_protocols TLSv1 TLSv1.1 TLSv1.2;<br />     # to create this, see https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html<br />     ssl_dhparam /etc/ssl/certs/dhparam.pem;<br />     keepalive_timeout 20s;</blockcode></code><blockcode></blockcode><blockcode><br /><code>    server_name [your domain];<br />     root /home/data/[your domain];<br />     index index.php;</code></blockcode><blockcode><br /><code>    # change the file name of these logs to include your server name<br />     # if hosting many services...<br />     access_log /var/log/nginx/[your domain]_access.log;<br />     error_log /var/log/nginx/[your domain]_error.log;</code></blockcode><blockcode><br /><br /><code>    location /notifications/hub/negotiate {<br />         proxy_pass http://127.0.0.1:8080;<br />         proxy_set_header Upgrade $http_upgrade;<br />         proxy_set_header Connection "upgrade";<br />         proxy_set_header Host $http_host;<br />         proxy_set_header X-Real-IP $remote_addr;<br />         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;<br />         proxy_set_header X-Forwarded-Host $server_name;<br />         proxy_set_header X-Forwarded-Proto https;<br />         proxy_connect_timeout 2400;<br />         proxy_read_timeout 2400;<br />         proxy_send_timeout 2400;<br />     }</code></blockcode><blockcode><br /><code>    location / {<br />         proxy_pass http://127.0.0.1:8080;<br />         proxy_set_header Upgrade $http_upgrade;<br />         proxy_set_header Connection "upgrade";<br />         proxy_set_header Host $http_host;<br />         proxy_set_header X-Real-IP $remote_addr;<br />         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;<br />         proxy_set_header X-Forwarded-Host $server_name;<br />         proxy_set_header X-Forwarded-Proto https;<br />         proxy_connect_timeout 2400;<br />         proxy_read_timeout 2400;<br />         proxy_send_timeout 2400;<br />     }</code></blockcode><blockcode><br /><code>    location /notifications/hub {<br />         proxy_pass http://127.0.0.1:3012;<br />         proxy_set_header Upgrade $http_upgrade;<br />         proxy_set_header Connection "upgrade";<br />     }<br />     #<br />     # These "harden" your security<br />     add_header 'Access-Control-Allow-Origin' "*";<br /> }</code></blockcode></p> </li> <li>You should now be able to run<br /><code>nginx -t </code><br /> again, and it you haven't got an accidental errors in the files, it should return no errors. You can restart nginx to make sure it picks up your SSL certificates...<br /><code>service nginx restart</code></li> </ol><p>Now everything is read to set up your BitWarden Docker containers!</p> <h3>Setting up your BitWarden "rust" service</h3> <p>Before we start this part, you'll need a few bits of information. First, you'll need a 64 character random string to be your "admin token"... you can create that like this:<br /><code>pwgen -y 64 1</code></p> <p>copy the result (highlight the text and hit CTRL+SHIFT+C) and paste it somewhere so you can copy-and-paste it into the file below later.</p> <p>Also, if you want your BitWarden server to be able to send out emails, like for password recovery, you'll need to have an "authenticating SMTP email account"... I would recommend setting one up specifically for this purpose. You can use a random gmail account or any other email account that lets you send mail by logging into an SMTP (Simple Mail Transfer Protocol) server, i.e. most mail servers. You'll need to know the SMTP [host name], the [port] (usually 465 or 587), the [login security] (usually "true" or "TLS"), and your authenticating [username] (possibly this is also the email address) and [password]. You'll also need a "[from email] like bitwarden@[your domain] or similar, which will be the sender of email from your server.</p> <p>You're going to be setting up your configuration in the directory we created earlier, so run<br /><code>cd /home/docker/$DOMAIN</code></p> <p>and there<br /><code>$EDIT docker-compose.yml</code></p> <p>copy-and-pasting in the following, replacing the [tokens] appropriately:</p> <p><blockcode><code>version: "3"<br /> services:<br />     app:<br />         image: bitwardenrs/server<br />         environment:<br />             - DOMAIN=https://[your domain]<br />             - WEBSOCKET_ENABLED=true<br />             - SIGNUPS_ALLOWED=false<br />             - LOG_FILE="/data/bitwarden.log"<br />             - INVITATIONS_ALLOWED=true<br />             - ADMIN_TOKEN=[admin token]<br />             - SMTP_HOST=[host name]<br />             - SMTP_FROM=[from email]<br />             - SMTP_PORT=[port]<br />             - SMTP_SSL=[login security]<br />             - SMTP_USERNAME=[username]<br />             - SMTP_PASSWORD=[password]<br />         volumes:<br />             - /home/data/[your domain]/data/:/data/<br />         ports:<br />             - "127.0.0.1:8080:80"<br />             - "127.0.0.1:3012:3012"<br />         restart:<br />             unless-stopped</code></blockcode></p> <p><em>Note that the indentation has to be exact in this file - Docker Compose will complain otherwise.</em></p> <p>With the docker-compose file completed, you're ready to "pull" your package!</p> <p><code>docker-compose pull</code></p> <p>This will download the BitWarden Docker container from hub.docker.com. Then all you need to do is start it:</p> <p><code>docker-compose up -d &amp;&amp; docker-compose logs -f</code></p> <p>the "up -d" option actually starts the container called "app" which is actually your BitWarden rust server in "daemon" mode, which means it'll keep running unless you tell it to stop. If that's successful, it automatically then shows you the logs of that container. You can exit at any time with CTRL-C which will put you back on the command prompt. If you <em>do</em> want the container to stop, just run</p> <p><code>docker-compose stop</code></p> <p>If your start up was successful, you should see a message like this (albeit your version number could be higher - 1.9.0 is the current version of the Rust implementation at the time of writing):</p> <p><blockcode><code>/--------------------------------------------------------------------\<br /> |                       Starting Bitwarden_RS                        |<br /> |                           Version 1.9.0                            |<br /> |--------------------------------------------------------------------|<br /> | This is an *unofficial* Bitwarden implementation, DO NOT use the   |<br /> | official channels to report bugs/features, regardless of client.   |<br /> | Report URL: https://github.com/dani-garcia/bitwarden_rs/issues/new |<br /> \--------------------------------------------------------------------/</code></blockcode></p> <p>You should now be able to point your browser at <code>http://[your domain]</code> which, in turn, should automatically redirect you to <code><strong>https://</strong>[your domain]</code> and you should see the BitWarden web front end similar to that shown in the attached screen shot!</p> <h3>First Login!</h3> <p>To do your initial login by going to <code><strong>https://</strong>[your domain]<strong>/admin/</strong> and</code> you'll be asked to provide your "admin token" (a random string you created earlier for your docker-compose.yml file, where you should be able to find it) to create a first user with administration privileges. That will allow you to create your initial personal user and other useful stuff.</p> <p>For additional info on setting up these services - and new options as Daniel and his co-developers add them in - consult the <a href="https://github.com/dani-garcia/bitwarden_rs">repository pages</a> and <a href="https://github.com/dani-garcia/bitwarden_rs/issues">issues</a> and for Docker-specific questions, look at <a href="https://github.com/mprasil/bitwarden_rs">mpasil's pages</a>.</p> <h3>Sending Emails</h3> <p>It'll be worth testing if your email services work, like by requesting a password hint! You should be able to see what the server's doing via the</p> <p><code>docker-compose logs -f</code></p> <h2>Tips</h2> <p>I recommend <em>not </em>including your login credentials to your BitWarden instance in your BitWarden database ;) that's the one thing you need to remember. If you need to write it down somewhere, then do so (but make sure you don't include <em>all</em> the info needed to log in on the same piece of paper, that's just asking for trouble).</p> <p>Also, you can easily configure all the BitWarden clients - browser plugins, mobile apps, or the desktop app -  to use your server rather than BitWarden's default hosted service. Just click the "gear" settings icon on each app's interface, and set the "Self-Hosted Environment" Server URL to be your server, i.e. https://[your domain]</p> <h3>Backing it all up</h3> <p>I've created a SQLite backup script (which maintains automatic versioned hourly, daily, weekly, monthly, and yearly database dumps, the content in which is encrypted) <a href="/automatic-versioned-backups-sqlite-docker-compose-container">described in more detail in another post</a>...</p> <h3>Two Factor Authentication</h3> <p>This configuration should allow you to simply turn on Two Factor Authentication for any given BitWarden user.</p> <h3>Keeping it up-to-date</h3> <p>One of the best things about this Docker configuration is that it's very straightforward to upgrade your installation to Daniel's (via mpasil's Docker work) latest server version. Just log into the server as your unprivileged user,</p> <p><code>cd /home/docker/[your domain]<br /> docker-compose  pull<br /> docker-compose up -d &amp;&amp; docker-compose logs -f</code></p> <p>The whole process shouldn't take much more than a minute, with a few seconds downtime only as your new Docker BitWarden container is being created...</p> <p>Hope this helps a few folks! If you find any of the above doesn't work, please let me know in the comments. I'll do my best to make sure this how-to is accurate and up-to-date, and I'll do my best to assist people if they're having trouble.</p> <p>Have (secure and private) fun!t</p> </div> </div> </div> <section class="field field-node--field-blog-comments field-name-field-blog-comments field-type-comment field-label-above comment-wrapper"> <a name="comments"></a> <h2 class="comment-field__title">Blog comments</h2> <article data-comment-user-id="0" id="comment-164" about="/comment/164" typeof="schema:Comment" class="comment js-comment by-anonymous has-title clearfix"> <div class="comment__container"> <h3 property="schema:name" datatype="" class="comment__title"> <a href="/comment/164#comment-164" class="permalink" rel="bookmark" hreflang="en">Excellent blog</a> <span class="comment__new marker marker--success hidden" data-comment-timestamp="1571734038"></span> </h3> <div class="comment__meta"> <div class="comment__submitted"> <span class="comment__author"><span rel="schema:author"><span lang="" typeof="schema:Person" property="schema:name" datatype="">cocoonkid (not verified)</span></span> </span> <span class="comment__pubdate">Wed 09/10/2019 - 00:19 <span property="schema:dateCreated" content="2019-10-08T11:19:00+00:00" class="rdf-meta hidden"></span> </span> </div> </div> <div class="comment__content"> <div property="schema:text" class="clearfix text-formatted field field-comment--comment-body field-name-comment-body field-type-text-long field-label-hidden"> <div class="field__items"> <div property="schema:text" class="field__item"><p>Really appreciate the document. </p> <p>Would love to see these too:</p> <p>To do your initial login, I believe (I&#039;ll test this and update this howto!) you&#039;ll be asked to provide your &quot;admin token&quot; to create a first user with administration privileges.</p> <p>&amp;</p> <p>I&#039;ll add information on my SQLite backup scripts (which maintain automatic versioned hourly, daily, weekly, monthly, and yearly database dumps, the content in which is encrypted)...</p> <p>Thanks!</p> </div> </div> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=164&amp;1=default&amp;2=en&amp;3=" token="SQxJH8_hmUFGP5a1HuPe5YGDeqKAH7IJWa-lrTf5AVs"></drupal-render-placeholder> </div> </div> </article> <div class="indented"><article data-comment-user-id="1" id="comment-167" about="/comment/167" typeof="schema:Comment" class="comment js-comment by-node-author has-title clearfix"> <div class="comment__container"> <h3 property="schema:name" datatype="" class="comment__title"> <a href="/comment/167#comment-167" class="permalink" rel="bookmark" hreflang="en">Fair call! Leave it with me…</a> <span class="comment__new marker marker--success hidden" data-comment-timestamp="1571734079"></span> </h3> <div class="comment__meta"> <div class="comment__submitted"> <span class="comment__author"><span rel="schema:author"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">dave</span></span> </span> <span class="comment__pubdate">Tue 22/10/2019 - 21:47 <span property="schema:dateCreated" content="2019-10-22T08:47:59+00:00" class="rdf-meta hidden"></span> </span> </div> </div> <div class="comment__content"> <p class="comment__parent visually-hidden">In reply to <a href="/comment/164#comment-164" class="permalink" rel="bookmark" hreflang="en">Excellent blog</a> by <span lang="" typeof="schema:Person" property="schema:name" datatype="">cocoonkid (not verified)</span></p> <div property="schema:text" class="clearfix text-formatted field field-comment--comment-body field-name-comment-body field-type-text-long field-label-hidden"> <div class="field__items"> <div property="schema:text" class="field__item"><p>Fair call! Leave it with me...</p> </div> </div> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=167&amp;1=default&amp;2=en&amp;3=" token="5EsOUo5fz0dhfO2bRk_l-ywwj6OH5l8p9nz86QNDu_A"></drupal-render-placeholder> </div> </div> </article> <div class="indented"><article data-comment-user-id="1" id="comment-342" about="/comment/342" typeof="schema:Comment" class="comment js-comment by-node-author has-title clearfix"> <div class="comment__container"> <h3 property="schema:name" datatype="" class="comment__title"> <a href="/comment/342#comment-342" class="permalink" rel="bookmark" hreflang="en">Backup script update...</a> <span class="comment__new marker marker--success hidden" data-comment-timestamp="1581631657"></span> </h3> <div class="comment__meta"> <div class="comment__submitted"> <span class="comment__author"><span rel="schema:author"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">dave</span></span> </span> <span class="comment__pubdate">Fri 14/02/2020 - 11:07 <span property="schema:dateCreated" content="2020-02-13T22:07:37+00:00" class="rdf-meta hidden"></span> </span> </div> </div> <div class="comment__content"> <p class="comment__parent visually-hidden">In reply to <a href="/comment/167#comment-167" class="permalink" rel="bookmark" hreflang="en">Fair call! Leave it with me…</a> by <span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">dave</span></p> <div property="schema:text" class="clearfix text-formatted field field-comment--comment-body field-name-comment-body field-type-text-long field-label-hidden"> <div class="field__items"> <div property="schema:text" class="field__item"><p>I've published a new post which describes the SQLite backup script and points to the publicly visible git repo, linked in the article now. I've also updated the question of the admin code...</p> </div> </div> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=342&amp;1=default&amp;2=en&amp;3=" token="ukuAKkHDAuErmOn-ice3ptjHTOoqGV13cGbtjg82D9k"></drupal-render-placeholder> </div> </div> </article> </div></div><article data-comment-user-id="0" id="comment-788" about="/comment/788" typeof="schema:Comment" class="comment js-comment by-anonymous has-title clearfix"> <div class="comment__container"> <h3 property="schema:name" datatype="" class="comment__title"> <a href="/comment/788#comment-788" class="permalink" rel="bookmark" hreflang="en">Thank You!</a> <span class="comment__new marker marker--success hidden" data-comment-timestamp="1599790208"></span> </h3> <div class="comment__meta"> <div class="comment__submitted"> <span class="comment__author"><span rel="schema:author"><span lang="" typeof="schema:Person" property="schema:name" datatype="">SS (not verified)</span></span> </span> <span class="comment__pubdate">Tue 08/09/2020 - 07:06 <span property="schema:dateCreated" content="2020-09-07T19:06:51+00:00" class="rdf-meta hidden"></span> </span> </div> </div> <div class="comment__content"> <div property="schema:text" class="clearfix text-formatted field field-comment--comment-body field-name-comment-body field-type-text-long field-label-hidden"> <div class="field__items"> <div property="schema:text" class="field__item"><p>This is the best tutorial I&#039;ve come across for getting BitWarden up and running on a Pi.</p> <p>It worked the first time through. There a couple nice things that aren&#039;t obvious to the newbie that I tracked down after everything was up and running. Setting a docker restart policy for the BW container that ensures that the container comes up after a power interruption was a pleasant surprise. </p> <p>One thing that isn&#039;t clear to the absolute newbie is that it&#039;s probably better to set up on a subdomain rather than a domain if you want to use NGINX for serving other things. I wasn&#039;t thinking about that when I did the initial setup, and that&#039;s leading to rework... Ideally, I&#039;d like to have BitWarden sitting on a nonobvious subdomain, and serve up other things on the obvious ones, like the bare domain or <a href="http://www.example.xyz">www.example.xyz</a>.</p> <p>Again, thanks for a great post.</p> </div> </div> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=788&amp;1=default&amp;2=en&amp;3=" token="9n0zD1HbCjrKLmdvYGdyrEz07IZBRPpAphuhUIdiciQ"></drupal-render-placeholder> </div> </div> </article> <div class="indented"><article data-comment-user-id="1" id="comment-793" about="/comment/793" typeof="schema:Comment" class="comment js-comment by-node-author has-title clearfix"> <div class="comment__container"> <h3 property="schema:name" datatype="" class="comment__title"> <a href="/comment/793#comment-793" class="permalink" rel="bookmark" hreflang="en">Use a subdomain!</a> <span class="comment__new marker marker--success hidden" data-comment-timestamp="1599792389"></span> </h3> <div class="comment__meta"> <div class="comment__submitted"> <span class="comment__author"><span rel="schema:author"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">dave</span></span> </span> <span class="comment__pubdate">Fri 11/09/2020 - 14:46 <span property="schema:dateCreated" content="2020-09-11T02:46:29+00:00" class="rdf-meta hidden"></span> </span> </div> </div> <div class="comment__content"> <p class="comment__parent visually-hidden">In reply to <a href="/comment/788#comment-788" class="permalink" rel="bookmark" hreflang="en">Thank You!</a> by <span lang="" typeof="schema:Person" property="schema:name" datatype="">SS (not verified)</span></p> <div property="schema:text" class="clearfix text-formatted field field-comment--comment-body field-name-comment-body field-type-text-long field-label-hidden"> <div class="field__items"> <div property="schema:text" class="field__item"><p>Thanks for the suggestion, SS - I've added a note regarding using subdomains! And thanks for the positive feedback - hope your BitWarden is providing you with what you need!</p> </div> </div> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=793&amp;1=default&amp;2=en&amp;3=" token="mDd3QaMURnHddjJwNSFd8gkASpIOnAaSZq2qA6Z7QIM"></drupal-render-placeholder> </div> </div> </article> </div><article data-comment-user-id="0" id="comment-791" about="/comment/791" typeof="schema:Comment" class="comment js-comment by-anonymous has-title clearfix"> <div class="comment__container"> <h3 property="schema:name" datatype="" class="comment__title"> <a href="/comment/791#comment-791" class="permalink" rel="bookmark" hreflang="en">Login</a> <span class="comment__new marker marker--success hidden" data-comment-timestamp="1599790281"></span> </h3> <div class="comment__meta"> <div class="comment__submitted"> <span class="comment__author"><span rel="schema:author"><span lang="" typeof="schema:Person" property="schema:name" datatype="">Daniel Lamb (not verified)</span></span> </span> <span class="comment__pubdate">Fri 11/09/2020 - 04:20 <span property="schema:dateCreated" content="2020-09-10T16:20:37+00:00" class="rdf-meta hidden"></span> </span> </div> </div> <div class="comment__content"> <div property="schema:text" class="clearfix text-formatted field field-comment--comment-body field-name-comment-body field-type-text-long field-label-hidden"> <div class="field__items"> <div property="schema:text" class="field__item"><p>Hello,<br /> sorry might be being thick here but trying to login after follwing the setup guide and dont know what I should put in the username, I am using the token generated and in the yaml file.</p> <p>Daniel</p> </div> </div> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=791&amp;1=default&amp;2=en&amp;3=" token="2-gy49yCLhcMgXJLWSm-gmP574jf4pcpE4YajzKnBZ8"></drupal-render-placeholder> </div> </div> </article> <div class="indented"><article data-comment-user-id="1" id="comment-792" about="/comment/792" typeof="schema:Comment" class="comment js-comment by-node-author has-title clearfix"> <div class="comment__container"> <h3 property="schema:name" datatype="" class="comment__title"> <a href="/comment/792#comment-792" class="permalink" rel="bookmark" hreflang="en">First login...</a> <span class="comment__new marker marker--success hidden" data-comment-timestamp="1599792248"></span> </h3> <div class="comment__meta"> <div class="comment__submitted"> <span class="comment__author"><span rel="schema:author"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">dave</span></span> </span> <span class="comment__pubdate">Fri 11/09/2020 - 14:44 <span property="schema:dateCreated" content="2020-09-11T02:44:08+00:00" class="rdf-meta hidden"></span> </span> </div> </div> <div class="comment__content"> <p class="comment__parent visually-hidden">In reply to <a href="/comment/791#comment-791" class="permalink" rel="bookmark" hreflang="en">Login</a> by <span lang="" typeof="schema:Person" property="schema:name" datatype="">Daniel Lamb (not verified)</span></p> <div property="schema:text" class="clearfix text-formatted field field-comment--comment-body field-name-comment-body field-type-text-long field-label-hidden"> <div class="field__items"> <div property="schema:text" class="field__item"><p>Hi Daniel,</p> <p>Sorry that I wasn't clear - to be honest, I'm not sure what the latest versions use for the first login, but from memory, the very first visitor to a new BitWarden site sees a special login form asking them to enter their Admin Token which then allows them to set up their normal user login details (an email address and passphrase)... If you <em>don't </em>see something like that, then I would expect you could remove the Sqlite db file forcing a reinitialisation of the system.</p> </div> </div> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=792&amp;1=default&amp;2=en&amp;3=" token="vah-Jl2qUpIYJyQ7onBYSC8CaaSRZgWoiMkqd4n-2B4"></drupal-render-placeholder> </div> </div> </article> </div> <div class="comment-form-wrapper"> <h2 class="comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=25&amp;2=field_blog_comments&amp;3=comment" token="hKQtrXBA7ErwoC_B3-AO9Vd7GHqkXf0X-QAroc-dhuE"></drupal-render-placeholder> </div> </section> Tue, 22 Jan 2019 22:30:13 +0000 dave 25 at http://tech.oeru.org http://tech.oeru.org/setting-your-own-bitwarden-password-keeper-and-sync-server#comments Democratising Higher Education with OERs & FOSS http://tech.oeru.org/democratising-higher-education-oers-foss <span class="field field--name-title field--type-string field--label-hidden">Democratising Higher Education with OERs &amp; FOSS</span> <div class="field field-node--field-blog-tags field-name-field-blog-tags field-type-entity-reference field-label-above"> <h3 class="field__label">Blog tags</h3> <div class="field__items"> <div class="field__item field__item--oeru18"> <span class="field__item-wrapper"><a href="/taxonomy/term/65" hreflang="en">oeru18</a></span> </div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">dave</span></span> <span class="field field--name-created field--type-created field--label-hidden">Mon 21/01/2019 - 14:02</span> <div class="clearfix text-formatted field field-node--body field-name-body field-type-text-with-summary field-label-hidden"> <div class="field__items"> <div class="field__item"><p>Rather than write a slide-based presentation for the Open Education miniconf at <a href="https://linux.conf.au" title="Linux.Conf.AU, the premier FOSS annual conference in the southern hemisphere... ">Linux.Conf.AU 2019</a>, I thought I'd try an approach that seemed to work quite well <a href="/2018-update-oeru-technology-stack">recently</a>: write my presentation as a blog post and make it available for posterity, before and after. You can find this post quickly by going to <strong>oer.nz/lca</strong> if you want to look at it on your own device.</p> <p>Update: If you'd like to see me presenting this talk, <a href="https://www.youtube.com/watch?v=o9KaDiHj-H4">the video is now available</a>. There're <a href="https://www.youtube.com/user/linuxconfau2019/videos">lots of other talks</a> from the conference, too - I encourage you to have a browse!</p> <p>So why am I here and what am I talking about?</p> <p>I'm here because I love building stuff. I'm also a fan of sharing, and of the Commons (both analogue and digital). Unsurprisingly, I'm a committed adherent to and advocate for the principles underlying Free and Open Source Software (FOSS) and Creative Commons licensing.</p> <p>As a person who has a lot of time working with technology and has had a great education thanks to a combination of luck, hard work, and privilege, I have realised that I have an opportunity to parley my good fortune to make the world a better place.</p> <p>After quite a bit of thinking about it, I've really only identified one thing that I feel confident is an unambiguously "good thing" to do: increasing opportunities in education, particularly for girls and women. So I<em> </em>focus my energies on<em> removing as many barriers as I can that hinder people all over the world from reaching their educational potentials</em>.</p> <h2>Living the Dream</h2> <p>After following a relatively <a href="https://davelane.nz">convoluted path</a> for many years, I consider myself very fortunate to be working in my dream role today: I'm the <a href="/insight-what-does-open-source-technologist-oer-foundation-do">Open Source Technologist</a> for the Open Educational Resource Foundation (OERF). I say it's my dream role because it allows me to support my family, work on something I find personally fulfilling because it's completely aligned with my personal interests, skills, and values, and gives me the huge satisfaction of knowing I'm doing something that will create opportunities for millions of people I've never even met.</p> <p>The OERF is a charitable foundation, hosted by the <a href="https://op.ac.nz">Otago Polytech</a> in Dunedin (South Island, New Zealand). I think it's fair to say we're a lean operation... with two full-time staff: the founder, <a href="https://wikieducator.org/User:Mackiwg" title="Formal bio for Wayne Mackintosh"><strong>Wayne Mackintosh</strong></a>, who's a committed FOSS-using academic and a genius at getting people to do the right thing (as well being the UNESCO chair in OER), and me - so the Open Source Technologist is the <em>only technologist</em>. You can start to see why I love this role.</p> <h2>Our Aims</h2> <p>The Foundation has three main aims:</p> <ol><li>to create the curriculum materials for an entire university's worth of academical "courses" that are entirely licensed as "<a href="https://en.wikipedia.org/wiki/Definition_of_Free_Cultural_Works" title="The definition of Free Cultural Works">Free Cultural Works</a>" (CC-By or CC-By-SA) - this is the curriculum analogue to FOSS.</li> <li>to create and lead a partnership with higher education institutions around the world to create a new type of higher education model. These partners recognise the value of collaborating on learning materials and are committed to expanding global access to high education, the articulation of academic credit, and see an opportunity in extending their assessment services to learners who aren't formally enrolled in their institutions.</li> <li>to create a platform for delivering these collaboratively assembled and accredited courses to <em>anyone who has access to the internet</em>, allow them to study those materials at no cost, and with low cost<em> formal assessment services</em> provided by our partners. This allows learners to gain <em>academic credit from accredited institutions,</em> potentially leading to a formal qualification. We encourage others (especially our partners) to copy our approach where it appeals to them.</li> </ol><h2>Radical Openness</h2> <p>Prior to my involvement, Wayne decided that the organisation would be "radically open". And he means it. How it works:</p> <ul><li>our constantly evolving strategic direction is <a href="https://wikieducator.org/images/3/32/OERu_strategic_plan_2018-2021.pdf">openly published</a> as a Free Cultural Work,</li> <li>all decision making is done collectively with our partners and the results (and thinking behind them) is proactively published,</li> <li>all our meetings are streamed, videoed, and made available for posterity, along with post meeting reports summarising the discussion and any decisions reached, and</li> <li><a href="/proprietary-software-holdouts">wherever possible</a>, the OERF uses FOSS (we all use Linux internally), contribute to upstream projects, and release any internally developed software as FOSS.</li> </ul><p>Here's an example of what it looks like: <a href="https://wikieducator.org/OERu/OERu_18.11_Meeting/Report#OERu_milestones">our 2018 partner meeting in Port Macqurie, NSW, Australia.</a></p> <h2>Associated Policies</h2> <p>In addition to its commitment to openness, the OERF undertakes its activities in accordance with a few general principles:</p> <ol><li>All our curriculum materials should be available for any educator to incorporate into any course an educator chooses, in a way that respects their authorship and the provenance of the materials.</li> <li>Most higher education institutions choose a monolithic learning management system (LMS) to frame learner interactions, allowing only authenticated learners to participate, and on matriculation they lose access to their learning materials and artefacts. Instead, we prefer a more flexible, open, and persistent environment that is also more relevant to learners: the Internet. </li> <li>No learners studying, nor educators contributing to, our course materials should be disadvantaged because they cannot afford proprietary software to do so, nor should they need to entrust any of their creative efforts, or personal privacy to any third party with a commercial interest.</li> <li> <p>This entire process should be fiscally sustainable, not based on pure charity, but due to enlightened self-interest from educational institutions (who realise they are in imminent danger of having their business models permanently disrupted).</p> </li> </ol><h2>Open, Reusable, Editable, Distributable content</h2> <p>All our curriculum materials should be available for any educator to incorporate into any course an educator chooses. We use the powerful (but relatively transparent to non-technical users) versioning capabilities of MediaWiki to version all content and provide an educators' analogue to a git repository. Our curriculum material site is <a href="https://wikieducator.org">WikiEducator.org</a> and it is usually among the top 100k sites on the web.</p> <h2>Loosely Coupled, no Monoliths</h2> <p>Rather than an LMS, we adhere to the "<a href="https://en.wikipedia.org/wiki/Unix_philosophy">Unix philosophy</a>" in our technology platform - all of our learning materials are delivered by a <a href="/many-simple-tools-loosely-coupled">loosely coupled</a> selection of excellent FOSS applications via the open Internet, and can be viewed by anyone, anonymously.</p> <p>Collaborating with other learners does require authentication to manage behaviour and demonstrate participation, however this is almost entirely done using FOSS web services we host.</p> <p>We select the software components for our "<a href="/2018-update-oeru-technology-stack">Next Generation Digital Learning Environment</a>" based on being FOSS licensed, web-based, open standards compliant, fit for purpose, mobile-friendly, and flexible (e.g. with respect to authentication methods).</p> <p>We also consider scalability - although our current learner numbers are low, in the hundreds or thousands, we expect to scale to hundreds of thousands in the coming year or two, and eventually to millions of learners.</p> <p>Thanks to our component model, if we identify a better FOSS component to fill a particular niche, we simply replace what's there already. Our solutions can evolve quickly (especially compared to most of academia) and relatively painlessly from our learners' perspective.</p> <h2>Respect People</h2> <p>To ensure we do not open our learners or collaborators to exploitation by corporate interests outside of our control... <em>we don't use any tools run by corporations </em>in our teaching process. We see them as largely incompatible with the <em>sharing</em> necessary for real teaching and learning. Our entire software stack is comprised of FOSS, including collaboration tools for both learners and educators.</p> <h2>Sustainable Model</h2> <p>Of course, as with any FOSS project, for the OERF's approach to succeed, it must appeal to the <em>enlightened self-interest</em> of its participants. Our aim, as a charitable foundation is not to profit, but to achieve sustainability while maximising our impact...</p> <p>Part of that is consistently achieving sufficient income and the other part is minimising costs. We have done the latter to the extent possible (our small staff overhead and an annual IT budget for software and infrastructure is $5000!). Other than a small but much appreciated grant from the Hewlett Foundation (we're a long time grantee), we are already self-sustaining.</p> <p>The major of our income comes from our partners' membership fees (which are about USD4000 per year, although some opt to pay somewhat more), which they choose to pay for a variety of different reasons.</p> <p>For some it's a matter of fulfilling their "greater good" mission. For others, it's a philosophical direction which appeals - improving access to education to those who currently have no realistic opportunities to pursue it due to their location, cultural restrictions, and personal circumstances.</p> <p>Many of our partners realise that their current situations are precarious - that they are in imminent danger of having their business models permanently disrupted by technologically savvy initiatives like ours  - it's in their best interest to be part of them.</p> <p>Many also recognise an opportunity in becoming familiar with the FOSS technologies that we're implementing and making available to our partners - if they choose to implement any one of our chosen technologies at their own institutions rather than a proprietary alternative, the cost savings alone (as well as increase capabilities) would easily compensate of many years worth of our membership fee.</p> <h2>The FOSS Stack</h2> <p>At a conference like this, people like to get specific about the technologies we use, so <a href="/2018-update-oeru-technology-stack">let's look into that</a> for the time we have left! Happy to talk to anyone further about all the details!</p> <h2> </h2> </div> </div> </div> <section class="field field-node--field-blog-comments field-name-field-blog-comments field-type-comment field-label-above comment-wrapper"> <a name="comments"></a> <h2 class="comment-field__title">Blog comments</h2> <article data-comment-user-id="0" id="comment-49" about="/comment/49" typeof="schema:Comment" class="comment js-comment by-anonymous has-title clearfix"> <div class="comment__container"> <h3 property="schema:name" datatype="" class="comment__title"> <a href="/comment/49#comment-49" class="permalink" rel="bookmark" hreflang="en">contribute</a> <span class="comment__new marker marker--success hidden" data-comment-timestamp="1560387860"></span> </h3> <div class="comment__meta"> <div class="comment__submitted"> <span class="comment__author"><span rel="schema:author"><span lang="" typeof="schema:Person" property="schema:name" datatype="">Stephen (not verified)</span></span> </span> <span class="comment__pubdate">Tue 28/05/2019 - 19:57 <span property="schema:dateCreated" content="2019-05-28T07:57:31+00:00" class="rdf-meta hidden"></span> </span> </div> </div> <div class="comment__content"> <div property="schema:text" class="clearfix text-formatted field field-comment--comment-body field-name-comment-body field-type-text-long field-label-hidden"> <div class="field__items"> <div property="schema:text" class="field__item"><p>This is very inspiring!</p> <p>Looking forward to chasing those links and learning more. I would love to contribute to these changes in education.</p> </div> </div> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=49&amp;1=default&amp;2=en&amp;3=" token="2bW4gH3KUsfISmgVb-IEas6wHmfqlnC3dv3_lvUWzdE"></drupal-render-placeholder> </div> </div> </article> <article data-comment-user-id="0" id="comment-50" about="/comment/50" typeof="schema:Comment" class="comment js-comment by-anonymous has-title clearfix"> <div class="comment__container"> <h3 property="schema:name" datatype="" class="comment__title"> <a href="/comment/50#comment-50" class="permalink" rel="bookmark" hreflang="en">Video</a> <span class="comment__new marker marker--success hidden" data-comment-timestamp="1567646367"></span> </h3> <div class="comment__meta"> <div class="comment__submitted"> <span class="comment__author"><span rel="schema:author"><span lang="" typeof="schema:Person" property="schema:name" datatype="">Jonathan Haack (not verified)</span></span> </span> <span class="comment__pubdate">Mon 05/08/2019 - 17:30 <span property="schema:dateCreated" content="2019-08-05T05:30:39+00:00" class="rdf-meta hidden"></span> </span> </div> </div> <div class="comment__content"> <div property="schema:text" class="clearfix text-formatted field field-comment--comment-body field-name-comment-body field-type-text-long field-label-hidden"> <div class="field__items"> <div property="schema:text" class="field__item"><p>Is this also posted on Archive.org? If so, can you share link? I will post more comments later after reading more closely and watching video - this is excellent. Jonathan Haack</p> </div> </div> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=50&amp;1=default&amp;2=en&amp;3=" token="mBBYRA2bSjbvhjATpZ349uybL4iAhfaDOtMHOwxP7oo"></drupal-render-placeholder> </div> </div> </article> <div class="indented"><article data-comment-user-id="1" id="comment-82" about="/comment/82" typeof="schema:Comment" class="comment js-comment by-node-author has-title clearfix"> <div class="comment__container"> <h3 property="schema:name" datatype="" class="comment__title"> <a href="/comment/82#comment-82" class="permalink" rel="bookmark" hreflang="en">Video</a> <span class="comment__new marker marker--success hidden" data-comment-timestamp="1567646664"></span> </h3> <div class="comment__meta"> <div class="comment__submitted"> <span class="comment__author"><span rel="schema:author"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">dave</span></span> </span> <span class="comment__pubdate">Thu 05/09/2019 - 13:24 <span property="schema:dateCreated" content="2019-09-05T01:24:24+00:00" class="rdf-meta hidden"></span> </span> </div> </div> <div class="comment__content"> <p class="comment__parent visually-hidden">In reply to <a href="/comment/50#comment-50" class="permalink" rel="bookmark" hreflang="en">Video</a> by <span lang="" typeof="schema:Person" property="schema:name" datatype="">Jonathan Haack (not verified)</span></p> <div property="schema:text" class="clearfix text-formatted field field-comment--comment-body field-name-comment-body field-type-text-long field-label-hidden"> <div class="field__items"> <div property="schema:text" class="field__item"><p>Sorry for the delayed response! Not sure if it's also on Archive.org - is that due to it being youtube and/or cloudflare? I agree that I'd prefer it was also available on PeerTube...</p> </div> </div> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=82&amp;1=default&amp;2=en&amp;3=" token="nVxxf7kfK3o9WWmH-jfoo0nGSrMi8Gxsp_WjifaM6w8"></drupal-render-placeholder> </div> </div> </article> </div> <div class="comment-form-wrapper"> <h2 class="comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=24&amp;2=field_blog_comments&amp;3=comment" token="mjUav4-YhA77NHR_0jlXW-3giZNhdMcjCjhc8ozAcyM"></drupal-render-placeholder> </div> </section> Mon, 21 Jan 2019 01:02:01 +0000 dave 24 at http://tech.oeru.org http://tech.oeru.org/democratising-higher-education-oers-foss#comments