Hi, Joel! I'd be happy to submit this as a PR or whatever is most preferred. You added me to the mojoliciousio repo, but then when I clicked on it, the invitation was gone -- not sure if that was intentional.
Anyway, attached in my article. I don't want to call it done, because my bonus sections aren't really done. I either need to do a little better, or remove them. Also, I have some footnotes at the beginning where I should put in some better information. Last, I'm not sure if mermaid markdown is going to render on the statocles site: but I thought it was a super cool way to include a markdown flow chart for a visualization. I edited my document using StackEdit.io, and mermaid was a supported feature there. So, I wanted to send this to you for a quick review for your feedback on those points or any others. Also, to show you that I was indeed serious about writing an article and I have indeed done so, it's basically ready to go today, I'd probably just want to remove the Bonuses if I were to do nothing else. I hope this is an article that can qualify well enough for the advent calendar! If it's the type of topic and writing that you had in mind, then I have a couple more ideas that I could try to crank out if you needed a few more slots filled. On Sat, Nov 3, 2018 at 3:54 PM Joel Berger <joel.a.ber...@gmail.com> wrote: > Hello Everyone, > > As I mentioned at Mojoconf, I am hoping to run the advent calendar again > this year, but I cannot do nearly as much work on it as last year, both for > sanity and personal reasons. Therefore I'm looking to you to help me out > with contributions. I've already gotten submissions from two authors, more > ideas from another few of you. Well now's the time! If you want in, and if > you want to be sure the calendar happens and is a success, start getting > those posts to me. I don't have a firm deadline, but I need probably 10 or > so in to me before the end of the month in order to go ahead, and probably > at least 10 more by publishing date, whenever that is. I'll commit to 5. > > As to topics, I think we have promises covered, indeed I'm going to send > out an email to those of you who want to do articles on promises so that we > can coordinate. I myself am taking the Async/Await pattern and hopefully > library announcement. > > Other topics can include anything from your library, your favorite > library, your favorite pattern, something you use Mojo for, or whatever > else you want to write about. Remember, even if what you do with > Perl/Mojo/The Web seems ordinary, it isn't ordinary to everyone else. We > all do different things and what seems mundane to you every day will be > fascinating to others. > > I'll be taking submissions in markdown format. Please use 4 space > indentation for code blocks as the markdown renderer hasn't caught up with > github flavor code fences. > > Please include a short but engaging title and at least a few paragraphs of > text. > > Every article should come either with an image or an idea for an image, > for the top banner and other marketing (think twitter). If that image is > included, I need to have a link to the source, which must be appropriately > licensed. Searching on wikimedia is a great way to start or else searching > on google images with the license filter on. See last year's calendar for > how that looks. > > If you didn't contribute last year, please include your full name (or how > you'd like to be credited), with a sentence or two about yourself, and a > gravatar url or image (if desired). > > You may submit as PRs to the github repo ( > https://github.com/jberger/mojolicious.io) or emailed to me. Don't worry > about getting the dates or metadata right, we'll figure that all out. And > please contact me if you have any trouble or questions. > > Cheers, > > Joel Berger > > P.S. If authors submit articles but we don't get enough, they will still > get published, it just may not be structured as a calendar. Don't worry, > contributions will not be wasted either way. > > P.P.S. ... but calendars are more fun, submit something and help us get > there! > > -- > You received this message because you are subscribed to the Google Groups > "Mojolicious" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to mojolicious+unsubscr...@googlegroups.com. > To post to this group, send email to mojolicious@googlegroups.com. > Visit this group at https://groups.google.com/group/mojolicious. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "Mojolicious" group. To unsubscribe from this group and stop receiving emails from it, send an email to mojolicious+unsubscr...@googlegroups.com. To post to this group, send email to mojolicious@googlegroups.com. Visit this group at https://groups.google.com/group/mojolicious. For more options, visit https://groups.google.com/d/optout.
# Automating Mojo::ACME for continuous free SSL certs  [Credits](https://commons.wikimedia.org/wiki/File:KUKA_robot_for_flat_glas_handling.jpg) ## Start with Why Having encrypted websites is a very important thing to do[^1], even Google is giving higher SEO ranking for it[^2]. With free certificates, there's really no excuse not to any more! Except that free certificate management is, in my opinion (due mostly to lack of wildcard support), still a pretty inconvenient task to do. It seems ACME protocol version 2 started supporting them in February 2018[^3], but as of November 2018, Mojo::ACME still does not[^4]. Keeping track of all the apps and their corresponding hostnames in SSL[^5] certificates is just a pain, so I like to centralize this responsibility to the gateway web server, and let the apps behind the protected gateway just be apps. If I feel I need to also encrypt traffic from gateway to internal app, I can do that too, and I'll typically just use self-signed certs which Mojolicious specifically is already more than equipped to do. [^1]: Why? [^2]: Oh yeah? [^3]: Prove it. [^4]: So why use Mojo::ACME? [^5]: Or is it better to say TLS? Handles multiple domains per cert, handles multiple certs (wildcards would be better/easier) Intended when used as a proxy_pass to another web server, specifically useful when you want to run other webservers like a php app or something Try this other built-in method if you're fortunate to run only Mojo apps The specific setup for which this article is intended is one in which there are one or more web servers behind a reverse proxy such as [nginx](http://nginx.org) and each web server runs one or more apps with each app handling requests for one or more FQDN. My typical setup is that I run a *production* Mojolicious [toadfarm](link) on one LXC, a *staging* toadfarm on another LXC, and a *development* Mojolicious daemon or prefork on another LXC. This will work just as well if the different web servers are on different VPS's or on the same VPS and not separated by containers. *[LXC]: Linux Container *[FQDN]: Fully Qualified Domain Name *[VPS]: Virtual Private Server > ### My deployment process > *Each of these deployment stages are a different web server **process** -- same box, different containers on the same box, different boxes across the galaxy... doesn't matter. What does matter is that each stage is a different **process** to ensure isolation between the stages. We don't want buggy code testing to affect other stages' web server process(es).* > - Develop on the development web server, and daemon / prefork are best for that. It provides the appropriate conveniences and ease of management, and should remain isolated from your users. I like to even pass port 3000 straight through the firewall to my development instance just for quick proof-of-concept testing. In general, I find that I still need to have a dedicated domain name and have it route through the standard port 80 because I do a lot of work with third-party APIs and many don't like to connect to a web server on a non-standard port. > - Deploy to staging. When I want others to see my development work, I then "freeze" my work at a given moment and "publish" it to staging which is treated just like production in terms of uptime expectancy (probably fewer 9s) and so I use the same tools (e.g. toadfarm) and care as I would with production -- the exception is that, of course, it isn't *production* production. My testing users expect to see a functional app and at their convenience -- it's a terrible idea to ever point users to your development box. You'll want to keep working and changing things and you can't wait for your users to have tried out your changes before you continue work on development. > - Finally, when users are happy with what they see in staging, deploy to production! > > *The techniques used for deploying are, for the purposes of this article, unimportant.* The intent of this article is to improve the convenience when it comes to managing one or more free ACME-based SSL certificates. By then end, we'll have one or more simple `cron.monthly` scripts that register a new ACME account if necessary and then (re-)generate the ACME-signed certificates. The gateway web server which already exists to reverse proxy to the web servers will intercept all non-encrypted port 80 traffic[^6] and redirect appropriate requests to the Mojo::ACME plugin. The cron.monthly scripts will update the reverse proxy configuration file based on a template. [^6]: All? First install some basic packages. This is on Ubuntu Bionic 18.04. ```bash $ sudo apt install \ cpanminus \ # We'll use this to install the Perl modules build-essential \ # gcc and make and other essential build utilities necessary for building some Perl packages libssl-dev \ # For IO::Socket::SSL zlib1g-dev # For Net::SSLeay which is needed for IO::Socket::SSL (It took me forever to track down this dependency, Net::SSLeay would just not install and with no good reason) $ sudo cpanm \ IO::Socket::SSL \ # Mojo::ACME and the SSL certificates that this article is about depends on this Mojolicious \ # No matter what you do in life, you need this Mojo::ACME \ # This is what is handling the certificate generation Mojolicious::Plugin::ACME::Command::acme::automate # This is what is automating that certificate generation (`automate`, for short) ``` *[automate]: Mojolicious::Plugin::ACME::Command::acme::automate We'll use nginx for the reverse proxy web server. ```bash $ sudo apt install nginx ``` ## mojo-acme-server mojo-acme-server is not a Perl module and it does not have an install script; therefore, it's currently advised to install to /opt. It's not "released" anywhere like CPAN or packaged like a .deb, so we'll just clone from git. ```bash $ cd /opt && \ sudo git clone g...@github.com:s1037989/mojo-acme-server.git && \ sudo chown -R ``whoami``.``whoami`` /opt/mojo-acme-server && \ cd /opt/mojo-acme-server ``` Copy the sample config file and edit it. ```bash $ sudo cp mojo-acme-server.conf.sample mojo-acme-server.conf && \ sudo $EDITOR mojo-acme-server.conf ``` Set the logfile, ssldir, and webdir locations as you see fit. `ssldir` is where the generated SSL certificate and private key files go. `webdir` is where the template-based reverse proxy config file goes. Copy the sample cron.monthly script to /etc/cron.monthly and edit it. ```bash $ sudo cp example/cron.monthly /etc/cron.monthly/acme && \ sudo $EDITOR /etc/cron.monthly/acme ``` You could put all your automate commands into the single acme script, or you could organize it into multiple scripts -- it makes no difference. No need to configure it to run more than once per month as ACME-signed certificates are good for 90 days. The most important options to set are -l (listen), -o (options), and the host(s). Set -l to listen the same as what nginx intends to proxy_pass to and (perhaps among other things) set the proxy_pass URL that the reverse proxy should proxy pass encrypted connections to for the domains on that certificate. Among other parameters, optionally set the template to use (-T). ```bash /opt/mojo-acme-server/mojo-acme-server acme automate \ # this is the `automate` Perl module we installed earlier -T nginx_default \ # You can have any number of templates, `automate` comes with a sample nginx config -l http://*:8928 \ # Set mojo-acme-server to listen on whatever random port (and make sure the `acme` nginx configuration directive points to a URL that this listen configuration will pick up. -o proxy_pass http://127.0.0.1:3000 \ # This is the URL to pass the encrypted traffic to, in this case it's my development Mojolicious daemon example.com www.example.com # These are the FQDN that this app will handle requests for ``` Templates are stored in templates/. Copy the sample and modify to your liking. ```bash $ sudo cp templates/nginx_default.ep.sample templates/nginx_default.ep && \ sudo $EDITOR templates/nginx_default.ep ``` Once a config file has been written, it won't be rewritten -- there's typically no reason to. Therefore, feel free to modify your individual generated config files to continue to optimize for that app's purposes. ## Nginx It would be super nice to have a single, simple nginx config file with variables instead of a new configuration directive for every certificate needed; but it's understandable why there's not: [It is too expensive to use variables in some nginx configuration directives](http://nginx.org/en/docs/faq/variables_in_config.html). Therefore, one of the main steps in automate is to generate a new template-based config file for each generated certificate -- it helps to keep the generated certificates and the corresponding reverse proxy configurations in sync. Real quick, modify the log format of nginx so you can quickly see which host is being requested. *(Why isn't this the default??)* In /etc/nginx/nginx.conf, define a log_format and then set the access log to use it. ``` log_format main '$remote_addr - $remote_user [$time_local] "$host" "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time'; access_log /var/log/nginx/access.log main; # Notice: it's calling the log format named `main` that was defined just above ``` Moving on, copy the sample `acme` nginx config file for handling the ACME protocol HTTP requests and edit it. ```bash $ cd /opt/mojo-acme-server && \ sudo cp example/nginx /etc/nginx/sites-available/acme && \ sudo ln -s /etc/nginx/sites-available/acme /etc/nginx/sites-enabled && \ sudo $EDITOR /etc/nginx/sites-available/acme ``` Most importantly, set the proxy_pass URL for where you configured mojo-acme-server to listen. In this article, mojo-acme-server is running on the same server instance as nginx, on random port 8928. We're telling nginx to proxy_pass data to the specified URL, and we'll later configure mojo-acme-server to listen on port 8928. nginx is listening on port 80/443 as it is *the* gateway for all of our web apps (Mojolicious, Apache/PHP, node.js, etc). ``` proxy_pass http://127.0.0.1:8928 ``` ## Visualize it ```mermaid graph TD Z[Internet] -- 1.2.3.4 --> A A[nginx gateway for<br>all the domains] -- port 80<br>ACME request --> B[nginx's acme config] A -- port 443<br>encrypted app request<br>example.com --> C[nginx's template-based config<br>for example.com] B -- proxy_pass http://127.0.0.1:8928 --> D[mojo-acme-server] C -- proxy_pass http://127.0.0.1:3000 --> E[mojo daemon] ``` ## Test it First, register your new ACME account and generate a new certificate. On success, the reverse proxy will be restarted. ```bash $ sudo /etc/cron.monthly/acme ``` That's it! The cron.monthly script runs the automate command that does all the automation! Test that your new certs have been generated well. ```bash $ openssl x509 -in /etc/ssl/mojo-acme-server-cert-example.com.crt -noout -text || echo Fail ``` If that fails, I got nothin' for ya, sorry. :( There's so much that could go wrong. In my experience, all of it in the ACME protocol stuff which lacks clear error messaging. That said, I built automate for the purpose of this article, so it's not exactly well tested or stressed. If it succeeds, proceed to access your newly encrypted website through a browser. Fire up a web server on the port that automate was configured to proxy_pass HTTP requests to. ```bash $ perl -Mojo -E \ 'a("/"=>sub{shift->render(text=>scalar localtime)})->start' \ # Simple app that just responds with the current time daemon \ # This is a complete Mojolicious web server, -l http://*:3000 # listening on port 3000 ``` Then load it up in your web browser. Do this on your personal workstation as opposed to the VPS instance running your nginx gateway. ```bash $ curl https://example.com ``` ## Final words At the end of this article is the perfect time to tell you that this whole process is deprecated. I'm sure there are other better ACME-handling tools out there (e.g. EFF's certbot), ones that already support wildcards through ACME v2. But I like to keep my servers as vanilla as possible and not install a bunch of other software and dependencies. I also want less to manage and fewer tools to understand deeply when it comes to my isolated Mojo app environments. This is also deprecated if Mojo::ACME ever gets wildcard support. It's possible that there will still be a little bit to automate, but the biggest reason for needing automation is for handling the oodles of certificates necessary for all of the hosts. One thing to probably continue to automate is running account registration and certificate generation together (why necessarily keep them separate?) and more importantly handle the launching of the ACME-required web server to handle the ACME protocol. It makes a lot of sense for a Mojo app to handle it's own ACME requests, but when these Mojo apps are all behind a proxy, that doesn't work out, and it just makes sense to centralize this effort to a single app. Common reasons to be behind a proxy are 1) limited public IPs and 2) needing to handle other non-Perl web apps (e.g. something php, like phpMyAdmin). It also might still make sense to maintain this after Mojo::ACME gets wildcard support just because there are plenty of use cases for hosting many completely different apps where separate certs are absolutely necessary; but it may make more sense to use a polished automation system like certbot. ## Bonus As a bonus, let's talk about Mojolicious' built-in SNI support. This works great if all you want to do is host Mojo apps and you want to `mount` them all into a single Mojo app (like with toadfarm). Configure your server's firewall to allow port 80/443 traffic (`ufw allow 80/tcp ; ufw allow 443/tcp`) and set your toadfarm / hypnotoad to listen on port 80/443 (`MOJO_LISTEN='http://*:80,https://*:443`). ## Final bonus I've been working on this, too... I'm not a web expert, as far as robust, general proxies go, this is probably a terrible idea. But for all of my use and normal web traffic testing, this seems to work well for me, maybe it should be said that this is most ideal for development. I've built a simple Mojolicious app that handles all of the ACME protocol stuff (through Mojo::ACME of course) and proxies to all Mojo app processes via unix sockets. Unix sockets are super fast and super easy to setup / manage. The benefit is that you can really scale your apps and not have one app take down the rest due to a memory leak or broken code. It's kind of like toadfarm, except that with toadfarm, a single Mojolicious process mounts all of the apps; whereas with this, each app can have its own process and user id and permissions and the requests get proxied to the app through unix sockets. The only app that can take down all the apps is the gateway proxy app. It's simple and minimal, there's no reason to ever touch it. It'll decrypt the traffic and pass it through to the appropriate unix socket. Then if someone hacks that app or it leaks crazy memory, none of the other apps need to be effected, including the proxy app. For me, this is very experimental, and mostly theory at this point. I'm mostly reserved about all of this because Mojolicious isn't (to my understanding) intended to handle proxying and therefore so much more may be necessary to truly handle proxying. I'm concerned that this technique is like writing another CMS or writing another HTML parser from scratch and using regular expressions to do it.