<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Julien Durillon, Author at Clever Cloud</title>
	<atom:link href="https://stagingv6.cleverapps.io/blog/author/julien-durillonclever-cloud-com/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>From Code to Product</description>
	<lastBuildDate>Fri, 27 Oct 2023 10:20:54 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2023/03/cropped-cropped-favicon-32x32.png</url>
	<title>Julien Durillon, Author at Clever Cloud</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Rethinking invoices to empower your accountant</title>
		<link>https://stagingv6.cleverapps.io/blog/features/2022/02/16/rethinking-invoices-to-empower-your-accountant/</link>
		
		<dc:creator><![CDATA[Julien Durillon]]></dc:creator>
		<pubDate>Wed, 16 Feb 2022 08:51:44 +0000</pubDate>
				<category><![CDATA[Features]]></category>
		<category><![CDATA[feature]]></category>
		<category><![CDATA[invoice]]></category>
		<category><![CDATA[invoicing]]></category>
		<guid isPermaLink="false">https://stagingv6.cleverapps.io/?p=5373</guid>

					<description><![CDATA[<p><img width="1600" height="617" src="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2022/02/refactoring-invoices-2.png" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="clever cloud new invoices" decoding="async" fetchpriority="high" srcset="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2022/02/refactoring-invoices-2.png 1600w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2022/02/refactoring-invoices-2-300x116.png 300w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2022/02/refactoring-invoices-2-1024x395.png 1024w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2022/02/refactoring-invoices-2-768x296.png 768w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2022/02/refactoring-invoices-2-1536x592.png 1536w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2022/02/refactoring-invoices-2-1368x528.png 1368w" sizes="(max-width: 1600px) 100vw, 1600px" /></p><!-- wp:paragraph -->
<p>Earlier this week, we released some changes in how we present our invoices. The result: invoices that help you understand what you pay and what to expect next time.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>As an introduction, let's describe our problem: Clever Cloud bills you for the services you used over a given period, and asks you to credit your account in advance to cover the next billing period. The next invoice will consume your prepaid credits and ask for more credits for the next period, and so on. Up until now, your credits balance at a given time was not clearly displayed. That was a problem. The other problem lies in the management of free credits. These free credits have an expiration date. We had to write this explicitly in the invoices.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Now that we have expressed our problem, let's dive into the solution! </p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="a-clearer-first-page">A clearer first page</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>First, we reworded items on the first page. In part to please our new legal service, but mostly because the existing terms were confusing. E.g. where you had "Clever Cloud consumption", you get "Platform usage fees". Add a few tweaks to make this first page easy to understand: we display a subtotal of everything you owe us for using the service (that's the new "Total fees" line). After that we display how much credits where removed or need to be added to your prepaid account. This gives us the grand total to pay.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":5377,"sizeSlug":"full","linkDestination":"none","className":"is-style-default"} -->
<figure class="wp-block-image size-full is-style-default"><img src="https://cdn.clever-cloud.com/uploads/2022/02/article-invoice-summary-example-1.png" alt="Screenshot of the first page of a new invoice. The content is described by the article" class="wp-image-5377"/><figcaption>Rethinked invoice's first page. Now with a highlighted total!</figcaption></figure>
<!-- /wp:image -->

<!-- wp:heading -->
<h2 id="explicating-free-credits">Explicating free credits</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>The next change appears on the second page: we give you a breakdown of all the <a href="https://stagingv6.cleverapps.io/#subscribe" target="_blank" rel="noreferrer noopener">free credits</a> consumed / expired this month and how we compute the credits to remove or to add to the invoice.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>On this second page you have: all the free credits you are entitled to, which ones have come to expire, how much you still have left to cover the next billing period. You also have all the variables we use to compute the added or consumed credits that show on the first page.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>This second page will help you keep track of your prepaid credits balance and have a better understanding of why we charge you this specific amount.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":5379,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img src="https://cdn.clever-cloud.com/uploads/2022/02/article-invoice-upfront-example.png" alt="Screenshot of an invoice second page, which states all the available / used coupons, all the free credits, then a breakdown of how we compute the credits to consume or refill in the invoice." class="wp-image-5379"/><figcaption>Can you spot what seems to be a computation error?</figcaption></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>With these changes, your accountant should be happy! Hopefully, this will help them understand our invoices and give them time they can spend trying to figure out invoices of your other providers! 😇</p>
<!-- /wp:paragraph -->]]></description>
										<content:encoded><![CDATA[<p><img width="1600" height="617" src="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2022/02/refactoring-invoices-2.png" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="clever cloud new invoices" decoding="async" srcset="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2022/02/refactoring-invoices-2.png 1600w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2022/02/refactoring-invoices-2-300x116.png 300w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2022/02/refactoring-invoices-2-1024x395.png 1024w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2022/02/refactoring-invoices-2-768x296.png 768w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2022/02/refactoring-invoices-2-1536x592.png 1536w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2022/02/refactoring-invoices-2-1368x528.png 1368w" sizes="(max-width: 1600px) 100vw, 1600px" /></p><!-- wp:paragraph -->
<p>Earlier this week, we released some changes in how we present our invoices. The result: invoices that help you understand what you pay and what to expect next time.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>As an introduction, let's describe our problem: Clever Cloud bills you for the services you used over a given period, and asks you to credit your account in advance to cover the next billing period. The next invoice will consume your prepaid credits and ask for more credits for the next period, and so on. Up until now, your credits balance at a given time was not clearly displayed. That was a problem. The other problem lies in the management of free credits. These free credits have an expiration date. We had to write this explicitly in the invoices.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Now that we have expressed our problem, let's dive into the solution! </p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="a-clearer-first-page">A clearer first page</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>First, we reworded items on the first page. In part to please our new legal service, but mostly because the existing terms were confusing. E.g. where you had "Clever Cloud consumption", you get "Platform usage fees". Add a few tweaks to make this first page easy to understand: we display a subtotal of everything you owe us for using the service (that's the new "Total fees" line). After that we display how much credits where removed or need to be added to your prepaid account. This gives us the grand total to pay.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":5377,"sizeSlug":"full","linkDestination":"none","className":"is-style-default"} -->
<figure class="wp-block-image size-full is-style-default"><img src="https://cdn.clever-cloud.com/uploads/2022/02/article-invoice-summary-example-1.png" alt="Screenshot of the first page of a new invoice. The content is described by the article" class="wp-image-5377"/><figcaption>Rethinked invoice's first page. Now with a highlighted total!</figcaption></figure>
<!-- /wp:image -->

<!-- wp:heading -->
<h2 id="explicating-free-credits">Explicating free credits</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>The next change appears on the second page: we give you a breakdown of all the <a href="https://stagingv6.cleverapps.io/#subscribe" target="_blank" rel="noreferrer noopener">free credits</a> consumed / expired this month and how we compute the credits to remove or to add to the invoice.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>On this second page you have: all the free credits you are entitled to, which ones have come to expire, how much you still have left to cover the next billing period. You also have all the variables we use to compute the added or consumed credits that show on the first page.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>This second page will help you keep track of your prepaid credits balance and have a better understanding of why we charge you this specific amount.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":5379,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img src="https://cdn.clever-cloud.com/uploads/2022/02/article-invoice-upfront-example.png" alt="Screenshot of an invoice second page, which states all the available / used coupons, all the free credits, then a breakdown of how we compute the credits to consume or refill in the invoice." class="wp-image-5379"/><figcaption>Can you spot what seems to be a computation error?</figcaption></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>With these changes, your accountant should be happy! Hopefully, this will help them understand our invoices and give them time they can spend trying to figure out invoices of your other providers! 😇</p>
<!-- /wp:paragraph -->]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Using JAAS in the Cloud Era</title>
		<link>https://stagingv6.cleverapps.io/blog/engineering/2020/09/09/using-jaas-in-the-cloud-era/</link>
		
		<dc:creator><![CDATA[Julien Durillon]]></dc:creator>
		<pubDate>Wed, 09 Sep 2020 15:44:00 +0000</pubDate>
				<category><![CDATA[Engineering]]></category>
		<category><![CDATA[Developers]]></category>
		<guid isPermaLink="false">https://www2.cleverapps.io/wp/blog/technology/2020/09/09/using-jaas-in-the-cloud-era/</guid>

					<description><![CDATA[<p><img width="1400" height="540" src="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/jaas-cloud-1.png" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="" decoding="async" srcset="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/jaas-cloud-1.png 1400w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/jaas-cloud-1-300x116.png 300w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/jaas-cloud-1-1024x395.png 1024w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/jaas-cloud-1-768x296.png 768w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/jaas-cloud-1-1368x528.png 1368w" sizes="(max-width: 1400px) 100vw, 1400px" /></p><p>In this article, we will learn how to configure JAAS authentication in a JVM application without resorting to configuration files. This is motivated by the fact that, in a cloud setup, persisting a configuration file to an ephemeral instance/container is not a good practice.</p>
<span id="more-2831"></span>

<h2 id="the-problem">The problem</h2>
<p>You are writing an application that depends on a <a href="https://zookeeper.apache.org/">ZooKeeper</a> cluster. The moment comes when you need to put your code into production. During the early development of your application you were running a ZooKeeper instance on your laptop. Now that you are deploying to production, you need a secure connection between your app and the production ZooKeeper cluster you just set up.</p>
<p>Reading the <a href="https://zookeeper.apache.org/doc/r3.6.1/zookeeperAdmin.html#sc_authOptions" title="ZooKeeper authentication options">ZooKeeper documentation</a> you learn that you can achieve that through multiple ways: X509, SASL Kerberos, SASL Digest. Since you just want to demonstrate your POC, you have no wish to take on the burden of using X509 client certificates or to deploy a Kerberos architecture. So you fallback to SASL Digest. It&#39;s simple and easy, it&#39;s a user/password pair like you are used to.</p>
<p>Reading <a href="https://cwiki.apache.org/confluence/display/ZOOKEEPER/Client-Server+mutual+authentication#ClientServermutualauthentication-ClientConfiguration" title="ZK Auth">some more documentation</a> you learn that you have to put the credentials in a jaas.conf file. You then look into <em>the internet</em> to find a way to bypass this requirement.</p>
<p>Well, your search is over, your quest ends with this very article!</p>
<p>You just want to copy some code and don&#39;t care about the whole &quot;let&#39;s talk about X&quot; part? Jump to the <a href="#how-to-get-rid-of-jaas-config-file">&quot;How to get rid of jaas.config file&quot;</a> section!</p>
<h2 id="some-explanations-first">Some explanations first</h2>
<p>Since Java 1.4, a security framework has been added to the JRE: the <a href="https://docs.oracle.com/en/java/javase/14/security/java-authentication-and-authorization-service-jaas-reference-guide.html#GUID-FA47090A-D270-4E4E-A5F6-752E1A48DC4C" title="JAAS Official doc">Java Authentication and Authorization Service (JAAS)</a>.</p>
<p>JAAS is a quite comprehensive and pluggable framework. The only downside is that it&#39;s thought as a service meant to authenticate &quot;the user running the code&quot;. So it has a tendency to use configuration files or even system JVM configuration.</p>
<p>Configuration based on files containing secrets is a growing no-go in the cloud era. Since most clouds rely on disposable systems, using a JAAS file means one of the following:</p>
<ol>
<li>commiting the file, with the secrets in it;</li>
<li>commiting a file and use pre-run hooks to set the right value in it;</li>
<li>running an external service that would provide this file.</li>
</ol>
<p>Solution 1 is a strict no-go. Solution 2 is doable, but as a developer, I don&#39;t like having to resort to such practices. However, it&#39;s still better than solution 1, I&#39;m just stating my very personal point on view! Solution 3 is a bit like solution 2. It needs some kind of pre-run hook. In addition, it needs to trust the external source.</p>
<p>In this article, we will see how to abstract ourselves from the file-based JAAS login configuration. I will use a ZooKeeper Java/Scala client as an example.</p>
<h2 id="whats-in-the-box-jaas-file">What&#39;s in the <del>box</del> JAAS file?</h2>
<p>First, what is a JAAS file made of?</p>
<p>As the <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/tutorials/LoginConfigFile.html">Oracle documentation</a> states, it is made of <em>entries</em>. Each <em>entry</em> is composed of multiple <em>modules</em>. Each <em>module</em> provides a login/authorization mechanism with all its options. An authentication/authorization process has to satisfy all <em>modules</em> of the <em>entry</em> to succeed.</p>
<h3 id="example">Example</h3>
<p>We assume that the server part is already configured. Here we focus on the client part only.</p>
<p>Let&#39;s say we want to authenticate a Java application against a ZooKeeper cluster using the DIGEST-MD5 <a href="https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer" title="Wikipedia SASL">SASL</a> mechanism. We write a jaas.conf file somewhere on the filesystem. Here is what the file content will look like:</p>
<pre><code class="language-text">Client {
   org.apache.zookeeper.server.auth.DigestLoginModule required
   username=&quot;buffysummers&quot;
   password=&quot;vampiresbeware&quot;;
};
</code></pre>
<p>The <em>entry</em> name is <code>Client</code> (the default client session name for ZooKeeper). There is one module identified by the FQDN of the <code>LoginModule</code> class that will process the authentication/authorization. This module is <code>required</code> and has two parameters: <code>username</code> and <code>password</code>. No spaces around the <code>=</code>, the semicolon separates the modules. So only one is needed at the end of the module. One is also needed after the entry.</p>
<p>&quot;Executing&quot; this entry is quite straightforward: Java instanciates the <code>org.apache.zookeeper.server.auth.DigestLoginModule</code> class and calls <code>initialize(&quot;subject&quot;, callback, sharedState, Map(username=buffysummers, password=vampirebeware))</code> on the instance. After that, the actual client will use the instance&#39;s methods to perform authentication/authorization.</p>
<p>It&#39;s the developer&#39;s responsibility to ensure the module class is present in the classpath. In the case of ZooKeeper, the library provides the <code>org.apache.zookeeper.server.auth.DigestLoginModule</code> class.</p>
<p>Oh, one last thing about the JAAS file syntax: for each <em>module</em> you must say if the module is required, sufficient, optional, etc. That&#39;s what the <code>required</code> keyword is about! That way you can require multiple authentication methods to pass or you can just ask for one of them to pass.</p>
<h3 id="how-does-it-work-in-the-jvm">How does it work in the JVM?</h3>
<p>When running your application, the client code will use the <code>javax.security.auth.login.Configuration</code> static class to access your configured JAAS. Actually, it will use the only JRE provided implementation <code>com.sun.security.auth.login.ConfigFile</code>, which parses your provided jaas.conf file.</p>
<p>The Configuration singleton provides one method, <code>getAppConfigurationEntry(String entryName)</code>. In our example, by default, the ZooKeeper client calls the method with the string &quot;Client&quot;.</p>
<p>In most JREs, there is no default JAAS file. That means <code>Configuration</code> has no entries.</p>
<h3 id="how-do-i-tell-the-jvm-where-to-find-jaasconf">How do I tell the JVM where to find jaas.conf?</h3>
<p>According to <a href="https://docs.oracle.com/en/java/javase/14/docs/api/jdk.security.auth/com/sun/security/auth/login/ConfigFile.html" title="Javadoc ConfigFile">the javadoc</a>, there are multiple ways to do that:</p>
<ul>
<li>edit the java.security system file (in <code>$JAVA_HOME</code>);</li>
<li>run your app with the java option <code>-Djava.security.auth.login.config=path/to/jaas.conf</code>;</li>
<li>put the file in the default location: <code>$HOME/.java.login.config</code>.</li>
</ul>
<p>The next section will show us how to setup your JVM using Java code, without relying on the filesystem. It will keep on using the ZooKeeper client example.</p>
<h2 id="how-to-get-rid-of-jaasconfig-file">How to get rid of jaas.config file</h2>
<p>The answer to that is quite simple: override Java&#39;s Configuration default implementation!</p>
<h3 id="disclaimer">Disclaimer</h3>
<p>Before we dive in the details, please note that <strong>There is only one Configuration object installed in the runtime at any given time</strong>. When we override it, we remove all previous login configurations. Most JREs do not have any default configuration, so you should be safe. However, if you deploy your application in your company&#39;s VMs or container, they may already have specific configuration. So ask around!</p>
<h3 id="implementing-a-new-configuration">Implementing a new Configuration</h3>
<p>First, we define a class that implements javax.security.auth.login.Configuration:</p>
<pre><code class="language-java">import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import java.util.HashMap;
import java.util.Map;

public class MyConfiguration extends Configuration {
   AppConfigurationEntry[] getAppConfigurationEntry(s: String) {
      Map&lt;String,String&gt; options = new HashMap&lt;&gt;();
      options.put(&quot;username&quot;, &quot;buffysummers&quot;);
      options.put(&quot;password&quot;, &quot;vampiresbeware&quot;);

      return {
         new AppConfigurationEntry(
               &quot;org.apache.zookeeper.server.auth.DigestLoginModule&quot;,
               AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
               options
               )
      };
   }
}
</code></pre>
<p>Of course, you are highly encouraged to get the values for <code>username</code> and <code>password</code> from the environment, from a config database, or whatever you want! You might even add a constructor and feed it the credentials. You do you!</p>
<p>The next step is to instanciate it. You need to do it before you create a new ZooKeeper client instance:</p>
<pre><code class="language-java">// 1. Do that somewhere *before* creating the ZooKeeper instance.
// I would say do it close enough so other people can understand why you do it, or
// document this call because it is kind of cryptic.
Configuration.setConfiguration(new MyConfiguration());

// 2. Just create the zookeeper instance as usual!
ZooKeeper zookeeper = new ZooKeeper(&quot;localhost:2181&quot;, 3600000, watchCallback);

// 3. ?

// 4. PROFIT!
</code></pre>
<p>And that&#39;s it.</p>
<h2 id="alternative-method">Alternative method</h2>
<p>When writing this article, I read the documentation a bit more in depth and found out that I could use Java properties in the JAAS configuration file!</p>
<p>So instead of the code-only solution, we could actually just commit a jaas.conf file containing:</p>
<pre><code class="language-text">Client {
   org.apache.zookeeper.server.auth.DigestLoginModule required
   username=&quot;${app.zookeeper.username}&quot;
   password=&quot;${app.zookeeper.password}&quot;;
};
</code></pre>
<p>Then run your Java application with the Java options: <code>-Dapp.zookeeper.username=buffysummers</code> and <code>-Dapp.zookeeper.password=vampirebeware</code>.</p>
<p>I tried it, it works!</p>
<p>Depending on your application, using this solution might be easier than the full-code one.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Now that we understand a bit more about JAAS and how to avoid using the file format, there is actually only one advice to keep in mind: <strong>DO NOT STORE CREDENTIALS IN YOUR GIT REPOSITORY!</strong> By using one of the two proposed solutions, you should be able to have the secrets provided by the environment.</p>
<p>Besides, both of them should work on Clever Cloud. Your environment variables are injected in your application both as environement and as Java properties! You can also define a variable named <code>app.zookeeper.username</code>. It will only appear as a Java property, due to limitations in environment variables names.</p>
]]></description>
										<content:encoded><![CDATA[<p><img width="1400" height="540" src="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/jaas-cloud-1.png" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="" decoding="async" loading="lazy" srcset="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/jaas-cloud-1.png 1400w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/jaas-cloud-1-300x116.png 300w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/jaas-cloud-1-1024x395.png 1024w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/jaas-cloud-1-768x296.png 768w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/jaas-cloud-1-1368x528.png 1368w" sizes="auto, (max-width: 1400px) 100vw, 1400px" /></p><p>In this article, we will learn how to configure JAAS authentication in a JVM application without resorting to configuration files. This is motivated by the fact that, in a cloud setup, persisting a configuration file to an ephemeral instance/container is not a good practice.</p>
<span id="more-2831"></span>

<h2 id="the-problem">The problem</h2>
<p>You are writing an application that depends on a <a href="https://zookeeper.apache.org/">ZooKeeper</a> cluster. The moment comes when you need to put your code into production. During the early development of your application you were running a ZooKeeper instance on your laptop. Now that you are deploying to production, you need a secure connection between your app and the production ZooKeeper cluster you just set up.</p>
<p>Reading the <a href="https://zookeeper.apache.org/doc/r3.6.1/zookeeperAdmin.html#sc_authOptions" title="ZooKeeper authentication options">ZooKeeper documentation</a> you learn that you can achieve that through multiple ways: X509, SASL Kerberos, SASL Digest. Since you just want to demonstrate your POC, you have no wish to take on the burden of using X509 client certificates or to deploy a Kerberos architecture. So you fallback to SASL Digest. It&#39;s simple and easy, it&#39;s a user/password pair like you are used to.</p>
<p>Reading <a href="https://cwiki.apache.org/confluence/display/ZOOKEEPER/Client-Server+mutual+authentication#ClientServermutualauthentication-ClientConfiguration" title="ZK Auth">some more documentation</a> you learn that you have to put the credentials in a jaas.conf file. You then look into <em>the internet</em> to find a way to bypass this requirement.</p>
<p>Well, your search is over, your quest ends with this very article!</p>
<p>You just want to copy some code and don&#39;t care about the whole &quot;let&#39;s talk about X&quot; part? Jump to the <a href="#how-to-get-rid-of-jaas-config-file">&quot;How to get rid of jaas.config file&quot;</a> section!</p>
<h2 id="some-explanations-first">Some explanations first</h2>
<p>Since Java 1.4, a security framework has been added to the JRE: the <a href="https://docs.oracle.com/en/java/javase/14/security/java-authentication-and-authorization-service-jaas-reference-guide.html#GUID-FA47090A-D270-4E4E-A5F6-752E1A48DC4C" title="JAAS Official doc">Java Authentication and Authorization Service (JAAS)</a>.</p>
<p>JAAS is a quite comprehensive and pluggable framework. The only downside is that it&#39;s thought as a service meant to authenticate &quot;the user running the code&quot;. So it has a tendency to use configuration files or even system JVM configuration.</p>
<p>Configuration based on files containing secrets is a growing no-go in the cloud era. Since most clouds rely on disposable systems, using a JAAS file means one of the following:</p>
<ol>
<li>commiting the file, with the secrets in it;</li>
<li>commiting a file and use pre-run hooks to set the right value in it;</li>
<li>running an external service that would provide this file.</li>
</ol>
<p>Solution 1 is a strict no-go. Solution 2 is doable, but as a developer, I don&#39;t like having to resort to such practices. However, it&#39;s still better than solution 1, I&#39;m just stating my very personal point on view! Solution 3 is a bit like solution 2. It needs some kind of pre-run hook. In addition, it needs to trust the external source.</p>
<p>In this article, we will see how to abstract ourselves from the file-based JAAS login configuration. I will use a ZooKeeper Java/Scala client as an example.</p>
<h2 id="whats-in-the-box-jaas-file">What&#39;s in the <del>box</del> JAAS file?</h2>
<p>First, what is a JAAS file made of?</p>
<p>As the <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/tutorials/LoginConfigFile.html">Oracle documentation</a> states, it is made of <em>entries</em>. Each <em>entry</em> is composed of multiple <em>modules</em>. Each <em>module</em> provides a login/authorization mechanism with all its options. An authentication/authorization process has to satisfy all <em>modules</em> of the <em>entry</em> to succeed.</p>
<h3 id="example">Example</h3>
<p>We assume that the server part is already configured. Here we focus on the client part only.</p>
<p>Let&#39;s say we want to authenticate a Java application against a ZooKeeper cluster using the DIGEST-MD5 <a href="https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer" title="Wikipedia SASL">SASL</a> mechanism. We write a jaas.conf file somewhere on the filesystem. Here is what the file content will look like:</p>
<pre><code class="language-text">Client {
   org.apache.zookeeper.server.auth.DigestLoginModule required
   username=&quot;buffysummers&quot;
   password=&quot;vampiresbeware&quot;;
};
</code></pre>
<p>The <em>entry</em> name is <code>Client</code> (the default client session name for ZooKeeper). There is one module identified by the FQDN of the <code>LoginModule</code> class that will process the authentication/authorization. This module is <code>required</code> and has two parameters: <code>username</code> and <code>password</code>. No spaces around the <code>=</code>, the semicolon separates the modules. So only one is needed at the end of the module. One is also needed after the entry.</p>
<p>&quot;Executing&quot; this entry is quite straightforward: Java instanciates the <code>org.apache.zookeeper.server.auth.DigestLoginModule</code> class and calls <code>initialize(&quot;subject&quot;, callback, sharedState, Map(username=buffysummers, password=vampirebeware))</code> on the instance. After that, the actual client will use the instance&#39;s methods to perform authentication/authorization.</p>
<p>It&#39;s the developer&#39;s responsibility to ensure the module class is present in the classpath. In the case of ZooKeeper, the library provides the <code>org.apache.zookeeper.server.auth.DigestLoginModule</code> class.</p>
<p>Oh, one last thing about the JAAS file syntax: for each <em>module</em> you must say if the module is required, sufficient, optional, etc. That&#39;s what the <code>required</code> keyword is about! That way you can require multiple authentication methods to pass or you can just ask for one of them to pass.</p>
<h3 id="how-does-it-work-in-the-jvm">How does it work in the JVM?</h3>
<p>When running your application, the client code will use the <code>javax.security.auth.login.Configuration</code> static class to access your configured JAAS. Actually, it will use the only JRE provided implementation <code>com.sun.security.auth.login.ConfigFile</code>, which parses your provided jaas.conf file.</p>
<p>The Configuration singleton provides one method, <code>getAppConfigurationEntry(String entryName)</code>. In our example, by default, the ZooKeeper client calls the method with the string &quot;Client&quot;.</p>
<p>In most JREs, there is no default JAAS file. That means <code>Configuration</code> has no entries.</p>
<h3 id="how-do-i-tell-the-jvm-where-to-find-jaasconf">How do I tell the JVM where to find jaas.conf?</h3>
<p>According to <a href="https://docs.oracle.com/en/java/javase/14/docs/api/jdk.security.auth/com/sun/security/auth/login/ConfigFile.html" title="Javadoc ConfigFile">the javadoc</a>, there are multiple ways to do that:</p>
<ul>
<li>edit the java.security system file (in <code>$JAVA_HOME</code>);</li>
<li>run your app with the java option <code>-Djava.security.auth.login.config=path/to/jaas.conf</code>;</li>
<li>put the file in the default location: <code>$HOME/.java.login.config</code>.</li>
</ul>
<p>The next section will show us how to setup your JVM using Java code, without relying on the filesystem. It will keep on using the ZooKeeper client example.</p>
<h2 id="how-to-get-rid-of-jaasconfig-file">How to get rid of jaas.config file</h2>
<p>The answer to that is quite simple: override Java&#39;s Configuration default implementation!</p>
<h3 id="disclaimer">Disclaimer</h3>
<p>Before we dive in the details, please note that <strong>There is only one Configuration object installed in the runtime at any given time</strong>. When we override it, we remove all previous login configurations. Most JREs do not have any default configuration, so you should be safe. However, if you deploy your application in your company&#39;s VMs or container, they may already have specific configuration. So ask around!</p>
<h3 id="implementing-a-new-configuration">Implementing a new Configuration</h3>
<p>First, we define a class that implements javax.security.auth.login.Configuration:</p>
<pre><code class="language-java">import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import java.util.HashMap;
import java.util.Map;

public class MyConfiguration extends Configuration {
   AppConfigurationEntry[] getAppConfigurationEntry(s: String) {
      Map&lt;String,String&gt; options = new HashMap&lt;&gt;();
      options.put(&quot;username&quot;, &quot;buffysummers&quot;);
      options.put(&quot;password&quot;, &quot;vampiresbeware&quot;);

      return {
         new AppConfigurationEntry(
               &quot;org.apache.zookeeper.server.auth.DigestLoginModule&quot;,
               AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
               options
               )
      };
   }
}
</code></pre>
<p>Of course, you are highly encouraged to get the values for <code>username</code> and <code>password</code> from the environment, from a config database, or whatever you want! You might even add a constructor and feed it the credentials. You do you!</p>
<p>The next step is to instanciate it. You need to do it before you create a new ZooKeeper client instance:</p>
<pre><code class="language-java">// 1. Do that somewhere *before* creating the ZooKeeper instance.
// I would say do it close enough so other people can understand why you do it, or
// document this call because it is kind of cryptic.
Configuration.setConfiguration(new MyConfiguration());

// 2. Just create the zookeeper instance as usual!
ZooKeeper zookeeper = new ZooKeeper(&quot;localhost:2181&quot;, 3600000, watchCallback);

// 3. ?

// 4. PROFIT!
</code></pre>
<p>And that&#39;s it.</p>
<h2 id="alternative-method">Alternative method</h2>
<p>When writing this article, I read the documentation a bit more in depth and found out that I could use Java properties in the JAAS configuration file!</p>
<p>So instead of the code-only solution, we could actually just commit a jaas.conf file containing:</p>
<pre><code class="language-text">Client {
   org.apache.zookeeper.server.auth.DigestLoginModule required
   username=&quot;${app.zookeeper.username}&quot;
   password=&quot;${app.zookeeper.password}&quot;;
};
</code></pre>
<p>Then run your Java application with the Java options: <code>-Dapp.zookeeper.username=buffysummers</code> and <code>-Dapp.zookeeper.password=vampirebeware</code>.</p>
<p>I tried it, it works!</p>
<p>Depending on your application, using this solution might be easier than the full-code one.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Now that we understand a bit more about JAAS and how to avoid using the file format, there is actually only one advice to keep in mind: <strong>DO NOT STORE CREDENTIALS IN YOUR GIT REPOSITORY!</strong> By using one of the two proposed solutions, you should be able to have the secrets provided by the environment.</p>
<p>Besides, both of them should work on Clever Cloud. Your environment variables are injected in your application both as environement and as Java properties! You can also define a variable named <code>app.zookeeper.username</code>. It will only appear as a Java property, due to limitations in environment variables names.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Cellar v2: even more S3 compatibility</title>
		<link>https://stagingv6.cleverapps.io/blog/engineering/2019/06/20/cellar-s3-compatibility/</link>
		
		<dc:creator><![CDATA[Julien Durillon]]></dc:creator>
		<pubDate>Thu, 20 Jun 2019 15:35:00 +0000</pubDate>
				<category><![CDATA[Engineering]]></category>
		<category><![CDATA[changelog]]></category>
		<category><![CDATA[wpu]]></category>
		<guid isPermaLink="false">https://www2.cleverapps.io/wp/blog/technology/2019/06/20/cellar-s3-compatibility/</guid>

					<description><![CDATA[<p><img width="1400" height="540" src="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/06/cellarv2.png" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="" decoding="async" loading="lazy" srcset="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/06/cellarv2.png 1400w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/06/cellarv2-300x116.png 300w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/06/cellarv2-1024x395.png 1024w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/06/cellarv2-768x296.png 768w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/06/cellarv2-1368x528.png 1368w" sizes="auto, (max-width: 1400px) 100vw, 1400px" /></p><p>In November 2018, we released the Cellar C2 cluster. Every new cellar account since then uses the new cluster. Previously created add-ons remain on the old one.</p>
<span id="more-2822"></span>

<h2 id="cellar">Cellar?</h2>
<p>Cellar is our S3 add-on. You get the exact same API than S3, but the service is hosted by Clever Cloud. So you can use it with any S3 client. Even the official Amazon ones.</p>
<p>To achieve that, we use an open source implementation.</p>
<h2 id="the-differences-between-the-old-and-the-new-cellar">The differences between the old and the new Cellar</h2>
<p>That&#39;s a good question. See, at the beginning, we created the first Cellar add-on using <a href="https://docs.riak.com/riak/cs/latest/index.html">Riak CS</a> (then called Riak S2). Riak CS is an S3 API implementation on top of the Riak distributed NoSQL database by Basho.</p>
<p>We used it for quite some time. Then Amazon changed its authentication model, deprecating its Signature v2 algorithm in favor of the new Signature v4. We waited for Basho to update its implementation. Then Basho decided to quit maintaining Riak CS. So no new fancy Signature v4 for us.</p>
<p>With time, Amazon killed Signature v2 by rolling out new AWS APIs clients. It became clear we had to do something.</p>
<p>We had a look at different OSS project and decided to use <a href="https://ceph.com/">Ceph</a>.</p>
<p>Ceph is a distributed storage platform that comes with many interfaces:</p>
<ul>
<li>Object Store, supporting the S3 and Swift APIs.</li>
<li>Network Block Device, something we were already interested in.</li>
<li>Ceph FileSystem, a feature we used at the very beginning of Clever Cloud. It left us
with some mixed feeling. We replaced it quickly.</li>
</ul>
<p>Cellar C2 is now running on Ceph Object Store.</p>
<p>What does that mean, feature-wise? Well, let&#39;s compare it together:</p>
<figure>
  <table class="table">
    <thead>
      <tr>
        <th>Feature</th>
        <th>Riak CS</th>
        <th>Ceph Object Store</th>
      </tr>
    </thead>
    <tbody>
      <tr><td>List Buckets</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Delete Bucket</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Create Bucket</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Bucket Lifecycle</td><td></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Policy (Buckets, Objects)</td><td><abbr title="Partially Supported">•</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Bucket Website</td><td></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Bucket ACLs (GET, PUT)</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Bucket Location</td><td></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Bucket Notification</td><td></td><td></td></tr>
      <tr><td>Bucket Object Versions</td><td></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>GET Bucket Info (HEAD)</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Bucket Request Payment</td><td></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>PUT Object</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>PUT Object (Copy)</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>DELETE Object</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>GET Object</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Object ACLs (GET, PUT)</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>HEAD Object</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>POST Object</td><td></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Copy Object</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Multipart Uploads</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Object Tagging</td><td></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Signature v2</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Signature v4</td><td></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
    </tbody>
  </table>
  <figcaption>Feature comparison between Riak and Ceph.</figcaption>
</figure>

<p>The most important feature we gained was obviously the Signature v4 one.</p>
<h2 id="my-cellar-add-on-is-old-do-you-plan-to-migrate-it">My Cellar add-on is old. Do you plan to migrate it?</h2>
<p>For the moment, no. There is no easy way to move from Riak to Ceph. We plan to do it <em>when we get the time</em>. Until then, here is the way to do it:</p>
<pre><code class="language-bash">s3cmd -c old_s3cfg sync s3://bucket/ tmp/
s3cmd -c new_s3cfg sync tmp/ s3://bucket/
</code></pre>
<p>Feel free to <a href="mailto:support@clever-cloud.com">ask the support</a> (by email or via the console chat) for help and advice about moving your Cellar Bucket!</p>
]]></description>
										<content:encoded><![CDATA[<p><img width="1400" height="540" src="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/06/cellarv2.png" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="" decoding="async" loading="lazy" srcset="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/06/cellarv2.png 1400w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/06/cellarv2-300x116.png 300w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/06/cellarv2-1024x395.png 1024w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/06/cellarv2-768x296.png 768w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/06/cellarv2-1368x528.png 1368w" sizes="auto, (max-width: 1400px) 100vw, 1400px" /></p><p>In November 2018, we released the Cellar C2 cluster. Every new cellar account since then uses the new cluster. Previously created add-ons remain on the old one.</p>
<span id="more-2822"></span>

<h2 id="cellar">Cellar?</h2>
<p>Cellar is our S3 add-on. You get the exact same API than S3, but the service is hosted by Clever Cloud. So you can use it with any S3 client. Even the official Amazon ones.</p>
<p>To achieve that, we use an open source implementation.</p>
<h2 id="the-differences-between-the-old-and-the-new-cellar">The differences between the old and the new Cellar</h2>
<p>That&#39;s a good question. See, at the beginning, we created the first Cellar add-on using <a href="https://docs.riak.com/riak/cs/latest/index.html">Riak CS</a> (then called Riak S2). Riak CS is an S3 API implementation on top of the Riak distributed NoSQL database by Basho.</p>
<p>We used it for quite some time. Then Amazon changed its authentication model, deprecating its Signature v2 algorithm in favor of the new Signature v4. We waited for Basho to update its implementation. Then Basho decided to quit maintaining Riak CS. So no new fancy Signature v4 for us.</p>
<p>With time, Amazon killed Signature v2 by rolling out new AWS APIs clients. It became clear we had to do something.</p>
<p>We had a look at different OSS project and decided to use <a href="https://ceph.com/">Ceph</a>.</p>
<p>Ceph is a distributed storage platform that comes with many interfaces:</p>
<ul>
<li>Object Store, supporting the S3 and Swift APIs.</li>
<li>Network Block Device, something we were already interested in.</li>
<li>Ceph FileSystem, a feature we used at the very beginning of Clever Cloud. It left us
with some mixed feeling. We replaced it quickly.</li>
</ul>
<p>Cellar C2 is now running on Ceph Object Store.</p>
<p>What does that mean, feature-wise? Well, let&#39;s compare it together:</p>
<figure>
  <table class="table">
    <thead>
      <tr>
        <th>Feature</th>
        <th>Riak CS</th>
        <th>Ceph Object Store</th>
      </tr>
    </thead>
    <tbody>
      <tr><td>List Buckets</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Delete Bucket</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Create Bucket</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Bucket Lifecycle</td><td></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Policy (Buckets, Objects)</td><td><abbr title="Partially Supported">•</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Bucket Website</td><td></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Bucket ACLs (GET, PUT)</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Bucket Location</td><td></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Bucket Notification</td><td></td><td></td></tr>
      <tr><td>Bucket Object Versions</td><td></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>GET Bucket Info (HEAD)</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Bucket Request Payment</td><td></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>PUT Object</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>PUT Object (Copy)</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>DELETE Object</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>GET Object</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Object ACLs (GET, PUT)</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>HEAD Object</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>POST Object</td><td></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Copy Object</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Multipart Uploads</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Object Tagging</td><td></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Signature v2</td><td><abbr title="Supported" class="supported">✓</abbr></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
      <tr><td>Signature v4</td><td></td><td><abbr title="Supported" class="supported">✓</abbr></td></tr>
    </tbody>
  </table>
  <figcaption>Feature comparison between Riak and Ceph.</figcaption>
</figure>

<p>The most important feature we gained was obviously the Signature v4 one.</p>
<h2 id="my-cellar-add-on-is-old-do-you-plan-to-migrate-it">My Cellar add-on is old. Do you plan to migrate it?</h2>
<p>For the moment, no. There is no easy way to move from Riak to Ceph. We plan to do it <em>when we get the time</em>. Until then, here is the way to do it:</p>
<pre><code class="language-bash">s3cmd -c old_s3cfg sync s3://bucket/ tmp/
s3cmd -c new_s3cfg sync tmp/ s3://bucket/
</code></pre>
<p>Feel free to <a href="mailto:support@clever-cloud.com">ask the support</a> (by email or via the console chat) for help and advice about moving your Cellar Bucket!</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Announcing New PHP Versions</title>
		<link>https://stagingv6.cleverapps.io/blog/features/2016/01/15/announcing-new-php-versions/</link>
		
		<dc:creator><![CDATA[Julien Durillon]]></dc:creator>
		<pubDate>Fri, 15 Jan 2016 16:15:00 +0000</pubDate>
				<category><![CDATA[Features]]></category>
		<category><![CDATA[feature]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[release]]></category>
		<guid isPermaLink="false">https://www2.cleverapps.io/wp/blog/technology/2016/01/15/announcing-new-php-versions/</guid>

					<description><![CDATA[<p><img width="1400" height="540" src="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/php-multi-update-1.jpg" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="" decoding="async" loading="lazy" srcset="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/php-multi-update-1.jpg 1400w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/php-multi-update-1-300x116.jpg 300w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/php-multi-update-1-1024x395.jpg 1024w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/php-multi-update-1-768x296.jpg 768w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/php-multi-update-1-1368x528.jpg 1368w" sizes="auto, (max-width: 1400px) 100vw, 1400px" /></p><p>So, we <em>have</em> been a bit late when it came to supporting PHP 5.6. But we had a good reason for that! We were reworking the PHP scalers to create a single PHP scaler that could handle any PHP versions we would install on it. So we are finally getting PHP 5.6 and 7.0 out in the open.</p>
<p>But that&#39;s not all! Previously, selecting your PHP version was done upon application creation once and for all. Changing the PHP version required you to create a new application and to migrate your code (and data).</p>
<p>Now, you can change the PHP version by setting an environment variable and redeploying your application! Just set the PHP_VERSION variable to one of the following values:</p>
<ul>
<li>5.4</li>
<li>5.5</li>
<li>5.6</li>
<li>7.0</li>
</ul>
<p>Then redeploy your application, <em>et voilà</em>!</p>
<p>You can now test different versions of PHP as much as you want without setting up your environment every time! In the console, select the <code>PHP (beta)</code> application type when creating a new app. Once the beta will be over, you will see only one <code>PHP</code> option instead of <code>PHP 5.4</code> and <code>PHP 5.5</code>.</p>
]]></description>
										<content:encoded><![CDATA[<p><img width="1400" height="540" src="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/php-multi-update-1.jpg" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="" decoding="async" loading="lazy" srcset="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/php-multi-update-1.jpg 1400w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/php-multi-update-1-300x116.jpg 300w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/php-multi-update-1-1024x395.jpg 1024w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/php-multi-update-1-768x296.jpg 768w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/php-multi-update-1-1368x528.jpg 1368w" sizes="auto, (max-width: 1400px) 100vw, 1400px" /></p><p>So, we <em>have</em> been a bit late when it came to supporting PHP 5.6. But we had a good reason for that! We were reworking the PHP scalers to create a single PHP scaler that could handle any PHP versions we would install on it. So we are finally getting PHP 5.6 and 7.0 out in the open.</p>
<p>But that&#39;s not all! Previously, selecting your PHP version was done upon application creation once and for all. Changing the PHP version required you to create a new application and to migrate your code (and data).</p>
<p>Now, you can change the PHP version by setting an environment variable and redeploying your application! Just set the PHP_VERSION variable to one of the following values:</p>
<ul>
<li>5.4</li>
<li>5.5</li>
<li>5.6</li>
<li>7.0</li>
</ul>
<p>Then redeploy your application, <em>et voilà</em>!</p>
<p>You can now test different versions of PHP as much as you want without setting up your environment every time! In the console, select the <code>PHP (beta)</code> application type when creating a new app. Once the beta will be over, you will see only one <code>PHP</code> option instead of <code>PHP 5.4</code> and <code>PHP 5.5</code>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Redirect to HTTPS With Play 2.4</title>
		<link>https://stagingv6.cleverapps.io/blog/engineering/2015/12/01/redirect-to-https-in-play/</link>
		
		<dc:creator><![CDATA[Julien Durillon]]></dc:creator>
		<pubDate>Tue, 01 Dec 2015 17:55:00 +0000</pubDate>
				<category><![CDATA[Engineering]]></category>
		<category><![CDATA[Play!Framework]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SSL]]></category>
		<guid isPermaLink="false">https://www2.cleverapps.io/wp/blog/technology/2015/12/01/redirect-to-https-in-play/</guid>

					<description><![CDATA[<p><img width="1400" height="540" src="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/ssl-post-1.jpg" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="" decoding="async" loading="lazy" srcset="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/ssl-post-1.jpg 1400w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/ssl-post-1-300x116.jpg 300w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/ssl-post-1-1024x395.jpg 1024w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/ssl-post-1-768x296.jpg 768w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/ssl-post-1-1368x528.jpg 1368w" sizes="auto, (max-width: 1400px) 100vw, 1400px" /></p><p>To protect your web app or API, there is almost only one way at this time: TLS. But users and browsers don&#39;t always use TLS by default. So what you want is to redirect them to a TLS encrypted version of your site if they try to connect via plain http.</p>
<span id="more-2803"></span>
<p>Here is how to do it with Play Framework 2.4 in scala:</p>
<h2 id="play-and-http-filters">Play! and HTTP filters</h2>
<p>We will start by creating a &quot;TLSFilter.scala&quot; file and write a TLSFilter class in it:</p>
<pre><code class="language-scala">class TLSFilter extends Filter {
  def apply(nextFilter: RequestHeader =&gt; Future[Result])
    (requestHeader: RequestHeader): Future[Result] = {
      if(!requestHeader.secure)
        Future.successful(Results.MovedPermanently(&quot;https://&quot; + requestHeader.host + requestHeader.uri))
      else
        nextFilter(requestHeader).map(_.withHeaders(&quot;Strict-Transport-Security&quot; -&gt; &quot;max-age=31536000; includeSubDomains&quot;))
  }
}
</code></pre>
<p>This part is easy: we implement the apply function by just checking the <code>secure</code> value of <code>RequestHeader</code>. If the connection is not <em>secure</em>, we need to redirect the client to the same url only with &quot;https&quot; as the protocol. If the connection is <em>secure</em>, we pass the request to the next header. Nothing simpler.</p>
<p>Note that we use <code>requestHeader.host</code> instead of <code>requestHeader.domain</code> because the <code>host</code> value is actually the value of the <code>Host</code> header as set by the client, with optional port and stuff.</p>
<p>Note that we create a <code>Filter</code> implementation and not an <code>EssentialFilter</code> one because we do not care about the body.</p>
<p>Next, you need to create a <code>HttpFilters</code> implementation that will hold the instance of your <code>TLSFilter</code>:</p>
<pre><code class="language-scala">// In TLSFilter.scala
class MyFilters extends HttpFilters {
  val filters = Seq(new TLSFilter)
}
</code></pre>
<p>And finally, you need to tell Play! to use your <code>Filters</code> class:</p>
<pre><code class="language-properties"># In conf/application.conf
play.http.filters=my.package.MyFilters
</code></pre>
<p>Now it will check your requests and permamently redirect the clients to HTTPS.</p>
<p>But, how does Play! know that the request is <em>secure</em>?</p>
<h2 id="reverse-proxies-where-did-the-s-go">Reverse proxies: where did the &#39;s&#39; go?</h2>
<p>Now, we need to ensure that Play! knows to differentiate a secured connection from a plain one. If you configured HTTPS in your application, it&#39;s quite simple to understand. But it is not always the case:</p>
<p>You most probably did <em>not</em>, configure TLS in your application. And <em>that</em> is because <em>you deployed it on a very powerful and developer-friendly PaaS</em>. So, chances are your Play! application is getting requests in plain HTTP, because the TLS encryption ended at the front reverse-proxy that&#39;s piping the request towards your app.</p>
<p>How then is your application going to know that the connection is secured? Enter the non-standard and the standard ways.</p>
<h3 id="x-forwarded-proto">X-Forwarded-Proto</h3>
<p>The first, non-standard but widely used (e.g. at Clever Cloud) way to know if the request handled by the reverse proxy in front came in a secure channel is to check the <code>X-Forwarded-Proto</code> HTTP header. Like all non-standard headers, you can recognize it by the <code>X-</code> at the beginning of the name.</p>
<p>This header describes how the final client is communicating with the reverse-proxy.</p>
<p>It takes two values: <strong>http</strong> and <strong>https</strong>. You can check for that header in your application. But we will see below that Play! can do it by itself.</p>
<h3 id="rfc-2739">RFC 2739</h3>
<p>Also called the <em>Forwarded HTTP Extension</em>, it standardize the way that a proxy tells the final endpoint what is going on between the final client and itself. It&#39;s been published in June 2014.</p>
<p>You can read it here: <a href="https://tools.ietf.org/html/rfc7239">https://tools.ietf.org/html/rfc7239</a>. But the only thing that is relevant for us is the <code>proto</code> parameter. Like <code>X-Forwarded-Proto</code> earlier, its values that interest us are <strong>http</strong> and <strong>https</strong>. Like for the other one, Play! can handle those values for you, if you ask nicely.</p>
<h2 id="how-to-make-play-handle-forwarded-headers">How to make Play! handle Forwarded headers?</h2>
<p>At the time of this writing, Play! framework support for <code>Forwarded</code> headers have known many states:</p>
<ul>
<li>In Play! 1.x, you need to add <code>XForwardedSupport=all</code> in your application.conf</li>
<li>In Play! 2.0 to 2.3, you need to add <code>trustxforwarded=true</code> in your application.conf</li>
</ul>
<p>Both these ways only support the <code>X-Forwarded-Proto</code> header.</p>
<p>Now, in Play! 2.4.x, the philosophy is different:</p>
<ul>
<li>Define the version of the Forwarded header you want to use: <code>play.http.forwarded.version=x-forwarded|rfc7239</code></li>
<li>Set the proxies you trust: <code>play.http.forwarded.trustedProxies=[&quot;proxy_ip1&quot;,&quot;proxy_ip2&quot;,…]</code>.</li>
</ul>
<p>Of course <code>proxy_ipX</code> can be an actual IP or a subnet mask, like &quot;0.0.0.0&quot; or &quot;::&quot; to trust every IPv4 or v6, respectively. Defaults are &quot;127.0.0.1&quot; and &quot;::FF&quot;.</p>
<p>Also, as the <code>X-Forwarded-Proto</code> header is the one that is widely used in the world, the <code>version</code> default value is &quot;x-forwarded&quot;.</p>
<h2 id="what-the-hell-is-strict-transport-security">What the hell is Strict-Transport-Security?</h2>
<p>As you read the filter code, you must have seen that in the case the request is already in HTTPS, we still add a header to the response: <code>Strict-Transport-Security: max-age=31536000</code>.</p>
<p>This is the HTTP Strict Transport Security (HSTS) header. What it does is basically telling the client (most likely a browser): &quot;Next time (and for the next 31536000 seconds), if your user tries to load the unencrypted version of the site, don&#39;t wait for me to redirect you and use https already&quot;.</p>
<p>The browser (meaning: chrome &gt;= 4.0.211.0, firefox &gt;= 4.0, Opera &gt;= 12, IE &gt;= 11) will then save the website and automatically replace &quot;http&quot; by &quot;https&quot; in the requests the next times.</p>
<p>This mechanism is documented here: <a href="https://www.rfc-editor.org/rfc/rfc6797.txt">https://www.rfc-editor.org/rfc/rfc6797.txt</a>.</p>
<p>You <em>MUST</em> set the <code>max-age</code> value. You can also add <code>includeSubDomains</code> (after a &quot;;&quot; of course), which means &quot;if you get that header while requesting domain.com, please use HTTPS when requesting *.domain.com too&quot;. It is a good practice to always add <code>includeSubDomains</code> just in case.</p>
<p>Please note that the STS header can only be set if the website is already TLS protected. You <em>MUST NOT</em> set this header on a non-TLS response.</p>
<p>If you want the browsers to use HSTS before the first request, you can register your domain to be included in browsers <em>preload lists</em>. To achieve that, register your domain here: <a href="https://hstspreload.appspot.com/">https://hstspreload.appspot.com/</a>. Also add the <code>preload</code> value to the header, like that: <code>Strict-Transport-Security: max-age=31536000; preload</code>.</p>
]]></description>
										<content:encoded><![CDATA[<p><img width="1400" height="540" src="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/ssl-post-1.jpg" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="" decoding="async" loading="lazy" srcset="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/ssl-post-1.jpg 1400w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/ssl-post-1-300x116.jpg 300w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/ssl-post-1-1024x395.jpg 1024w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/ssl-post-1-768x296.jpg 768w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/ssl-post-1-1368x528.jpg 1368w" sizes="auto, (max-width: 1400px) 100vw, 1400px" /></p><p>To protect your web app or API, there is almost only one way at this time: TLS. But users and browsers don&#39;t always use TLS by default. So what you want is to redirect them to a TLS encrypted version of your site if they try to connect via plain http.</p>
<span id="more-2803"></span>
<p>Here is how to do it with Play Framework 2.4 in scala:</p>
<h2 id="play-and-http-filters">Play! and HTTP filters</h2>
<p>We will start by creating a &quot;TLSFilter.scala&quot; file and write a TLSFilter class in it:</p>
<pre><code class="language-scala">class TLSFilter extends Filter {
  def apply(nextFilter: RequestHeader =&gt; Future[Result])
    (requestHeader: RequestHeader): Future[Result] = {
      if(!requestHeader.secure)
        Future.successful(Results.MovedPermanently(&quot;https://&quot; + requestHeader.host + requestHeader.uri))
      else
        nextFilter(requestHeader).map(_.withHeaders(&quot;Strict-Transport-Security&quot; -&gt; &quot;max-age=31536000; includeSubDomains&quot;))
  }
}
</code></pre>
<p>This part is easy: we implement the apply function by just checking the <code>secure</code> value of <code>RequestHeader</code>. If the connection is not <em>secure</em>, we need to redirect the client to the same url only with &quot;https&quot; as the protocol. If the connection is <em>secure</em>, we pass the request to the next header. Nothing simpler.</p>
<p>Note that we use <code>requestHeader.host</code> instead of <code>requestHeader.domain</code> because the <code>host</code> value is actually the value of the <code>Host</code> header as set by the client, with optional port and stuff.</p>
<p>Note that we create a <code>Filter</code> implementation and not an <code>EssentialFilter</code> one because we do not care about the body.</p>
<p>Next, you need to create a <code>HttpFilters</code> implementation that will hold the instance of your <code>TLSFilter</code>:</p>
<pre><code class="language-scala">// In TLSFilter.scala
class MyFilters extends HttpFilters {
  val filters = Seq(new TLSFilter)
}
</code></pre>
<p>And finally, you need to tell Play! to use your <code>Filters</code> class:</p>
<pre><code class="language-properties"># In conf/application.conf
play.http.filters=my.package.MyFilters
</code></pre>
<p>Now it will check your requests and permamently redirect the clients to HTTPS.</p>
<p>But, how does Play! know that the request is <em>secure</em>?</p>
<h2 id="reverse-proxies-where-did-the-s-go">Reverse proxies: where did the &#39;s&#39; go?</h2>
<p>Now, we need to ensure that Play! knows to differentiate a secured connection from a plain one. If you configured HTTPS in your application, it&#39;s quite simple to understand. But it is not always the case:</p>
<p>You most probably did <em>not</em>, configure TLS in your application. And <em>that</em> is because <em>you deployed it on a very powerful and developer-friendly PaaS</em>. So, chances are your Play! application is getting requests in plain HTTP, because the TLS encryption ended at the front reverse-proxy that&#39;s piping the request towards your app.</p>
<p>How then is your application going to know that the connection is secured? Enter the non-standard and the standard ways.</p>
<h3 id="x-forwarded-proto">X-Forwarded-Proto</h3>
<p>The first, non-standard but widely used (e.g. at Clever Cloud) way to know if the request handled by the reverse proxy in front came in a secure channel is to check the <code>X-Forwarded-Proto</code> HTTP header. Like all non-standard headers, you can recognize it by the <code>X-</code> at the beginning of the name.</p>
<p>This header describes how the final client is communicating with the reverse-proxy.</p>
<p>It takes two values: <strong>http</strong> and <strong>https</strong>. You can check for that header in your application. But we will see below that Play! can do it by itself.</p>
<h3 id="rfc-2739">RFC 2739</h3>
<p>Also called the <em>Forwarded HTTP Extension</em>, it standardize the way that a proxy tells the final endpoint what is going on between the final client and itself. It&#39;s been published in June 2014.</p>
<p>You can read it here: <a href="https://tools.ietf.org/html/rfc7239">https://tools.ietf.org/html/rfc7239</a>. But the only thing that is relevant for us is the <code>proto</code> parameter. Like <code>X-Forwarded-Proto</code> earlier, its values that interest us are <strong>http</strong> and <strong>https</strong>. Like for the other one, Play! can handle those values for you, if you ask nicely.</p>
<h2 id="how-to-make-play-handle-forwarded-headers">How to make Play! handle Forwarded headers?</h2>
<p>At the time of this writing, Play! framework support for <code>Forwarded</code> headers have known many states:</p>
<ul>
<li>In Play! 1.x, you need to add <code>XForwardedSupport=all</code> in your application.conf</li>
<li>In Play! 2.0 to 2.3, you need to add <code>trustxforwarded=true</code> in your application.conf</li>
</ul>
<p>Both these ways only support the <code>X-Forwarded-Proto</code> header.</p>
<p>Now, in Play! 2.4.x, the philosophy is different:</p>
<ul>
<li>Define the version of the Forwarded header you want to use: <code>play.http.forwarded.version=x-forwarded|rfc7239</code></li>
<li>Set the proxies you trust: <code>play.http.forwarded.trustedProxies=[&quot;proxy_ip1&quot;,&quot;proxy_ip2&quot;,…]</code>.</li>
</ul>
<p>Of course <code>proxy_ipX</code> can be an actual IP or a subnet mask, like &quot;0.0.0.0&quot; or &quot;::&quot; to trust every IPv4 or v6, respectively. Defaults are &quot;127.0.0.1&quot; and &quot;::FF&quot;.</p>
<p>Also, as the <code>X-Forwarded-Proto</code> header is the one that is widely used in the world, the <code>version</code> default value is &quot;x-forwarded&quot;.</p>
<h2 id="what-the-hell-is-strict-transport-security">What the hell is Strict-Transport-Security?</h2>
<p>As you read the filter code, you must have seen that in the case the request is already in HTTPS, we still add a header to the response: <code>Strict-Transport-Security: max-age=31536000</code>.</p>
<p>This is the HTTP Strict Transport Security (HSTS) header. What it does is basically telling the client (most likely a browser): &quot;Next time (and for the next 31536000 seconds), if your user tries to load the unencrypted version of the site, don&#39;t wait for me to redirect you and use https already&quot;.</p>
<p>The browser (meaning: chrome &gt;= 4.0.211.0, firefox &gt;= 4.0, Opera &gt;= 12, IE &gt;= 11) will then save the website and automatically replace &quot;http&quot; by &quot;https&quot; in the requests the next times.</p>
<p>This mechanism is documented here: <a href="https://www.rfc-editor.org/rfc/rfc6797.txt">https://www.rfc-editor.org/rfc/rfc6797.txt</a>.</p>
<p>You <em>MUST</em> set the <code>max-age</code> value. You can also add <code>includeSubDomains</code> (after a &quot;;&quot; of course), which means &quot;if you get that header while requesting domain.com, please use HTTPS when requesting *.domain.com too&quot;. It is a good practice to always add <code>includeSubDomains</code> just in case.</p>
<p>Please note that the STS header can only be set if the website is already TLS protected. You <em>MUST NOT</em> set this header on a non-TLS response.</p>
<p>If you want the browsers to use HSTS before the first request, you can register your domain to be included in browsers <em>preload lists</em>. To achieve that, register your domain here: <a href="https://hstspreload.appspot.com/">https://hstspreload.appspot.com/</a>. Also add the <code>preload</code> value to the header, like that: <code>Strict-Transport-Security: max-age=31536000; preload</code>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Rails 4 apps backed by Sidekiq + Clever Cloud.</title>
		<link>https://stagingv6.cleverapps.io/blog/engineering/2015/05/05/deploy-a-rails-app-using-sidekiq-on-clever-cloud/</link>
		
		<dc:creator><![CDATA[Julien Durillon]]></dc:creator>
		<pubDate>Tue, 05 May 2015 15:32:00 +0000</pubDate>
				<category><![CDATA[Engineering]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">https://www2.cleverapps.io/wp/blog/technology/2015/05/05/deploy-a-rails-app-using-sidekiq-on-clever-cloud/</guid>

					<description><![CDATA[<p><img width="1400" height="540" src="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/rails4-sidekiq-1.jpg" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="" decoding="async" loading="lazy" srcset="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/rails4-sidekiq-1.jpg 1400w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/rails4-sidekiq-1-300x116.jpg 300w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/rails4-sidekiq-1-1024x395.jpg 1024w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/rails4-sidekiq-1-768x296.jpg 768w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/rails4-sidekiq-1-1368x528.jpg 1368w" sizes="auto, (max-width: 1400px) 100vw, 1400px" /></p><p>At Clever Cloud, we want developers to be happy. To achieve that we try to adapt as quickly as we can to users needs. The other day, someone asked for <a href="http://sidekiq.org/">Sidekiq</a> support for their Rails applications.</p>
<span id="more-2798"></span>

<h2 id="whats-sidekiq">What&#39;s Sidekiq</h2>
<p>Sidekiq is a solution to run long background tasks in ruby. It works by running a background server and treating jobs stored in a Redis. It&#39;s helpful to delay tasks, take a long API call to the background while still be able to answer to a request in a timely manner.</p>
<h2 id="how-do-i-use-sidekiq-on-clever-cloud">How do I use Sidekiq on Clever Cloud?</h2>
<p>First, you need a Redis instance. Start by provisioning a redis addon on Clever Cloud. Remember to link the redis add-on to your application in the Console.</p>
<p>The redis addon provides a <code>REDIS_URL</code> environment variable which will be used by sidekiq to connect to the redis addon.</p>
<ul>
<li>The next and only step is to add/modify the <code>clevercloud/ruby.json</code> file to add the
&quot;sidekiq&quot; flag:</li>
</ul>
<pre><code class="language-json">{
   &quot;deploy&quot;: {
      &quot;sidekiq&quot;: true
   }
}
</code></pre>
<p>Then commit and push your application, and you should be all set!</p>
]]></description>
										<content:encoded><![CDATA[<p><img width="1400" height="540" src="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/rails4-sidekiq-1.jpg" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="" decoding="async" loading="lazy" srcset="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/rails4-sidekiq-1.jpg 1400w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/rails4-sidekiq-1-300x116.jpg 300w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/rails4-sidekiq-1-1024x395.jpg 1024w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/rails4-sidekiq-1-768x296.jpg 768w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/rails4-sidekiq-1-1368x528.jpg 1368w" sizes="auto, (max-width: 1400px) 100vw, 1400px" /></p><p>At Clever Cloud, we want developers to be happy. To achieve that we try to adapt as quickly as we can to users needs. The other day, someone asked for <a href="http://sidekiq.org/">Sidekiq</a> support for their Rails applications.</p>
<span id="more-2798"></span>

<h2 id="whats-sidekiq">What&#39;s Sidekiq</h2>
<p>Sidekiq is a solution to run long background tasks in ruby. It works by running a background server and treating jobs stored in a Redis. It&#39;s helpful to delay tasks, take a long API call to the background while still be able to answer to a request in a timely manner.</p>
<h2 id="how-do-i-use-sidekiq-on-clever-cloud">How do I use Sidekiq on Clever Cloud?</h2>
<p>First, you need a Redis instance. Start by provisioning a redis addon on Clever Cloud. Remember to link the redis add-on to your application in the Console.</p>
<p>The redis addon provides a <code>REDIS_URL</code> environment variable which will be used by sidekiq to connect to the redis addon.</p>
<ul>
<li>The next and only step is to add/modify the <code>clevercloud/ruby.json</code> file to add the
&quot;sidekiq&quot; flag:</li>
</ul>
<pre><code class="language-json">{
   &quot;deploy&quot;: {
      &quot;sidekiq&quot;: true
   }
}
</code></pre>
<p>Then commit and push your application, and you should be all set!</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Introducing io.js VS Node.js &#8211; Press Start</title>
		<link>https://stagingv6.cleverapps.io/blog/features/2015/01/23/introducing-io-js/</link>
		
		<dc:creator><![CDATA[Julien Durillon]]></dc:creator>
		<pubDate>Fri, 23 Jan 2015 15:22:00 +0000</pubDate>
				<category><![CDATA[Features]]></category>
		<category><![CDATA[feature]]></category>
		<category><![CDATA[iojs]]></category>
		<category><![CDATA[release]]></category>
		<guid isPermaLink="false">https://www2.cleverapps.io/wp/blog/technology/2015/01/23/introducing-io-js/</guid>

					<description><![CDATA[<p><img width="1400" height="540" src="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/iojs-1.jpg" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="" decoding="async" loading="lazy" srcset="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/iojs-1.jpg 1400w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/iojs-1-300x116.jpg 300w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/iojs-1-1024x395.jpg 1024w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/iojs-1-768x296.jpg 768w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/iojs-1-1368x528.jpg 1368w" sizes="auto, (max-width: 1400px) 100vw, 1400px" /></p><p>Change is happening in the Node landscape.<br>A group of rebels forked the main Node project, tired to wait for new versions and ES6 features. Their goal is to provide a npm compatible framework that always runs over the latest V8 version. The underlying goal is to profit from ES6 features that are being implemented in V8.  </p>
<p>Today, Clever Cloud gives you the possiblity to run your io.js app in the cloud.</p>
<span id="more-2857"></span>

<link href='//fonts.googleapis.com/css?family=Press+Start+2P' rel='stylesheet' type='text/css'>
<h2 class="quotes" style="font-family: 'Press Start 2P', cursive;">PRESS START</h2>

<p><em>A soundtrack is available for this post, in order to cope with its visual artwork:</em></p>
<iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/132124579&amp;auto_play=false&amp;hide_related=true&amp;player_type=tiny&amp;show_comments=flase&amp;show_user=false&amp;show_reposts=false&amp;visual=false"></iframe>

<h2 id="here-comes-the-rain-again">Here comes the rain again</h2>
<blockquote>
<p>It is inevitable. <small>Agent Smith, Matrix Reloaded (2003)</small></p>
</blockquote>
<p>Open Source generates diversity. Node.js fork io.js hits its version 1.0 (<a href="https://iojs.org/dist/v1.0.4/">1.0.4 now</a>). After a slight clash with Joyent last year, one of the Node.js developers, <a href="https://twitter.com/indutny">Fedor Indutny</a>, decided to go on his own to maintain a new version of Node.js.<br>As mentionned above, the goal it to support the features of version 6 of the <a href="http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf">ECMA-262 specification [PDF]</a>, which implementation timeframe was judged too slow on Node.js.</p>
<h2 id="why-is-supporting-iojs-great">Why is supporting io.js great?</h2>
<p>While it is great to have a production-ready environment, addressing stability and security concerns, we are still nerds. We love to use cutting edge technology. The io.js framewok just released version 1.0.4. This is not a stable version. It is just a version different enough from the node.js project that io people just felt they needed to express it in the version number.</p>
<h2 id="is-io-going-to-replace-nodejs-on-clever-cloud">Is io going to replace Node.js on Clever Cloud?</h2>
<p>No. We will go on supporting Node.js and io together. For now, the default is the latest Node.js version. In the future, the standard for server-side javascript might be io, or Node.js, or another framework that does not exist yet. It is still a young ecosystem.<br>Even if the entire community switches to another Node.js fork, we will still support Node.js for compatibility reasons.</p>
<h2 id="how-do-i-use-iojs-on-clever-cloud">How do I use io.js on Clever Cloud?</h2>
<p>It&#39;s fairly simple: as you specified the Node.js version in package.json, you just prefix the version with io.js:</p>
<pre><code class="language-javascript">{
  &quot;engines&quot;: {
    &quot;node&quot;: &quot;iojs-v1.0.3&quot;
  }
}
</code></pre>
<p>And that&#39;s it! The &quot;node&quot;  executable will be the one provided by io.js.</p>
<h2 id="what-can-i-do-with-iojs">What can I do with io.js?</h2>
<p>First, you can do everything you could do with Node.js. In addition to that, io.js is mostly about new features. Check the &quot;ES6 support&quot; page: <a href="https://iojs.org/es6.html">https://iojs.org/es6.html</a>.<br>You can use Map instead of Object, you have out-of-the box Promises/A+, you can (and will) use the &quot;let&quot; keyword…</p>
<p><a href="https://github.com/CleverCloud/demo-iojs">Try it now on Clever Cloud</a>!</p>
<p style="font-size:11px">Credits & Thanks to <a href="http://nervewax.com/" rel="noopener noreferrer" target="_blank">nervewax</a> for its great background image used for this post.</p>

<style type="text/css">
.quotes {display: none;}
@media (min-width: 1200px){
  .quotes {
    display: block;
    position: absolute;
    top: -200px;
    color: #14A2FC !important;
    text-align: center;
    width: 100%;
    animation: flickerAnimation 2s infinite;
    -webkit-animation: flickerAnimation 2.5s infinite;
  }
  @keyframes flickerAnimation {
    0%   { opacity:1; }
    50%  { opacity:0; }
    100% { opacity:1; }
  }
  @-o-keyframes flickerAnimation{
    0%   { opacity:1; }
    50%  { opacity:0; }
    100% { opacity:1; }
  }
  @-moz-keyframes flickerAnimation{
    0%   { opacity:1; }
    50%  { opacity:0; }
    100% { opacity:1; }
  }
  @-webkit-keyframes flickerAnimation{
    0%   { opacity:1; }
    50%  { opacity:0; }
    100% { opacity:1; }
  }
  .animate-flicker {
     -webkit-animation: flickerAnimation 1s infinite;
     -moz-animation: flickerAnimation 1s infinite;
     -o-animation: flickerAnimation 1s infinite;
      animation: flickerAnimation 1s infinite;
  }
}
</style>
]]></description>
										<content:encoded><![CDATA[<p><img width="1400" height="540" src="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/iojs-1.jpg" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="" decoding="async" loading="lazy" srcset="https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/iojs-1.jpg 1400w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/iojs-1-300x116.jpg 300w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/iojs-1-1024x395.jpg 1024w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/iojs-1-768x296.jpg 768w, https://staging-cc-assetsv6.cellar-c2.services.clever-cloud.com/uploads/2021/08/iojs-1-1368x528.jpg 1368w" sizes="auto, (max-width: 1400px) 100vw, 1400px" /></p><p>Change is happening in the Node landscape.<br>A group of rebels forked the main Node project, tired to wait for new versions and ES6 features. Their goal is to provide a npm compatible framework that always runs over the latest V8 version. The underlying goal is to profit from ES6 features that are being implemented in V8.  </p>
<p>Today, Clever Cloud gives you the possiblity to run your io.js app in the cloud.</p>
<span id="more-2857"></span>

<link href='//fonts.googleapis.com/css?family=Press+Start+2P' rel='stylesheet' type='text/css'>
<h2 class="quotes" style="font-family: 'Press Start 2P', cursive;">PRESS START</h2>

<p><em>A soundtrack is available for this post, in order to cope with its visual artwork:</em></p>
<iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/132124579&amp;auto_play=false&amp;hide_related=true&amp;player_type=tiny&amp;show_comments=flase&amp;show_user=false&amp;show_reposts=false&amp;visual=false"></iframe>

<h2 id="here-comes-the-rain-again">Here comes the rain again</h2>
<blockquote>
<p>It is inevitable. <small>Agent Smith, Matrix Reloaded (2003)</small></p>
</blockquote>
<p>Open Source generates diversity. Node.js fork io.js hits its version 1.0 (<a href="https://iojs.org/dist/v1.0.4/">1.0.4 now</a>). After a slight clash with Joyent last year, one of the Node.js developers, <a href="https://twitter.com/indutny">Fedor Indutny</a>, decided to go on his own to maintain a new version of Node.js.<br>As mentionned above, the goal it to support the features of version 6 of the <a href="http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf">ECMA-262 specification [PDF]</a>, which implementation timeframe was judged too slow on Node.js.</p>
<h2 id="why-is-supporting-iojs-great">Why is supporting io.js great?</h2>
<p>While it is great to have a production-ready environment, addressing stability and security concerns, we are still nerds. We love to use cutting edge technology. The io.js framewok just released version 1.0.4. This is not a stable version. It is just a version different enough from the node.js project that io people just felt they needed to express it in the version number.</p>
<h2 id="is-io-going-to-replace-nodejs-on-clever-cloud">Is io going to replace Node.js on Clever Cloud?</h2>
<p>No. We will go on supporting Node.js and io together. For now, the default is the latest Node.js version. In the future, the standard for server-side javascript might be io, or Node.js, or another framework that does not exist yet. It is still a young ecosystem.<br>Even if the entire community switches to another Node.js fork, we will still support Node.js for compatibility reasons.</p>
<h2 id="how-do-i-use-iojs-on-clever-cloud">How do I use io.js on Clever Cloud?</h2>
<p>It&#39;s fairly simple: as you specified the Node.js version in package.json, you just prefix the version with io.js:</p>
<pre><code class="language-javascript">{
  &quot;engines&quot;: {
    &quot;node&quot;: &quot;iojs-v1.0.3&quot;
  }
}
</code></pre>
<p>And that&#39;s it! The &quot;node&quot;  executable will be the one provided by io.js.</p>
<h2 id="what-can-i-do-with-iojs">What can I do with io.js?</h2>
<p>First, you can do everything you could do with Node.js. In addition to that, io.js is mostly about new features. Check the &quot;ES6 support&quot; page: <a href="https://iojs.org/es6.html">https://iojs.org/es6.html</a>.<br>You can use Map instead of Object, you have out-of-the box Promises/A+, you can (and will) use the &quot;let&quot; keyword…</p>
<p><a href="https://github.com/CleverCloud/demo-iojs">Try it now on Clever Cloud</a>!</p>
<p style="font-size:11px">Credits & Thanks to <a href="http://nervewax.com/" rel="noopener noreferrer" target="_blank">nervewax</a> for its great background image used for this post.</p>

<style type="text/css">
.quotes {display: none;}
@media (min-width: 1200px){
  .quotes {
    display: block;
    position: absolute;
    top: -200px;
    color: #14A2FC !important;
    text-align: center;
    width: 100%;
    animation: flickerAnimation 2s infinite;
    -webkit-animation: flickerAnimation 2.5s infinite;
  }
  @keyframes flickerAnimation {
    0%   { opacity:1; }
    50%  { opacity:0; }
    100% { opacity:1; }
  }
  @-o-keyframes flickerAnimation{
    0%   { opacity:1; }
    50%  { opacity:0; }
    100% { opacity:1; }
  }
  @-moz-keyframes flickerAnimation{
    0%   { opacity:1; }
    50%  { opacity:0; }
    100% { opacity:1; }
  }
  @-webkit-keyframes flickerAnimation{
    0%   { opacity:1; }
    50%  { opacity:0; }
    100% { opacity:1; }
  }
  .animate-flicker {
     -webkit-animation: flickerAnimation 1s infinite;
     -moz-animation: flickerAnimation 1s infinite;
     -o-animation: flickerAnimation 1s infinite;
      animation: flickerAnimation 1s infinite;
  }
}
</style>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Set up your SBT for personal proxy use</title>
		<link>https://stagingv6.cleverapps.io/blog/engineering/2013/11/30/set-up-sbt-for-proxy-use/</link>
		
		<dc:creator><![CDATA[Julien Durillon]]></dc:creator>
		<pubDate>Sat, 30 Nov 2013 00:00:00 +0000</pubDate>
				<category><![CDATA[Engineering]]></category>
		<guid isPermaLink="false">https://www2.cleverapps.io/wp/blog/technology/2013/11/30/set-up-sbt-for-proxy-use/</guid>

					<description><![CDATA[This post will cover the new Clever Cloud Artifactory instance we deployed two weeks ago, and how to set up SBT to make every client project use the proxy server, without the need of specific configuration for the client. Requirements To follow this post, we assume that you already know and use SBT. Nothing else [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>This post will cover the new Clever Cloud Artifactory instance we deployed two weeks ago, and how to set up SBT to make every client project use the proxy server, without the need of specific configuration for the client.</p>
<p><span id="more-2794"></span></p>
<h3 id="requirements">Requirements</h3>
<p>To follow this post, we assume that you already know and use <a href="http://scala-sbt.org/">SBT</a>. Nothing else is needed.</p>
<h3 id="what-is--why-use-artifactory">What is / Why use Artifactory?</h3>
<p><a href="http://www.jfrog.com/home/v_artifactory_opensource_overview">Artifactory Open Source</a> is an open source proxy and cache server for build automation and dependencies manager tools in the JVM world. It is what I call a &quot;lazy mirror&quot;. It acts like a Maven (or Ivy) server, and caches the artifacts &quot;for ever&quot;. It is not a proxy as described in RFC 2616. Eventually it becomes a mirror of the repositories it serves.</p>
<p>As the Clever Cloud scalers are stateless, they don&#39;t keep the dependencies of a project between two deployments. Therefore, every dependency has to be downloaded for each deployment. Maven having the (almost justified) reputation to download half the internet on a first run, a deployment can take ages because of the dependencies.</p>
<p>The first step we did was to initialize a local Maven (or Ivy, or SBT) cache with common plugins and dependencies. But maintaining an up-to-date image represents a lot of work. So, to speed up the download of dependencies, we needed a Maven (resp. Ivy) repository next to the scalers; that means in the same data center.</p>
<p>After considering various possibilities, the Artifactory solution was the best one:</p>
<ul>
<li>(Ridiculously) easy to set-up;</li>
<li>The open-source (and free) version matches our needs without extra<br />
complexity;</li>
<li>Supports high concurrency;</li>
<li>No need to watch for desynchronisation;</li>
<li>Can proxy any given repository (Maven, Ivy);</li>
<li>Does not act like a HTTP proxy but like a maven repository (that&#39;s the<br />
important point);</li>
</ul>
<p>So, eventually, <a href="http://maven.mirror.clvrcld.net:8080/artifactory/webapp/home.html?0">we started the server</a></p>
<h3 id="the-bad-part-client-configuration">The bad part: client configuration</h3>
<p>Setting up the server was as easy as dropping a war in an application server. Actually, it just consists of dropping a war in an application server, and following the (crystal clear, well written) documentation.</p>
<p>Setting up the client wasn&#39;t that easy.</p>
<h4 id="maven-client-configuration">Maven client configuration</h4>
<p>Maven was not a huge problem to set up. Some clicks in the artifactory public (anonymous) interface give you the following (you can happily remove the <code>servers</code> section):</p>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;settings xsi:schemaLocation=&quot;http://maven.apache.org/SETTINGS/1.1.0
http://maven.apache.org/xsd/settings-1.1.0.xsd&quot;
  xmlns=&quot;http://maven.apache.org/SETTINGS/1.1.0&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;&gt;
  &lt;profiles&gt;
   &lt;profile&gt;
    &lt;repositories&gt;
      &lt;repository&gt;
       &lt;snapshots&gt;
        &lt;enabled&gt;false&lt;/enabled&gt;
       &lt;/snapshots&gt;
       &lt;id&gt;central&lt;/id&gt;
       &lt;name&gt;libs-release&lt;/name&gt;
       &lt;url&gt;http://maven.mirror.clvrcld.net:8080/artifactory/libs-release&lt;/url&gt;
      &lt;/repository&gt;
      &lt;repository&gt;
       &lt;snapshots /&gt;
       &lt;id&gt;snapshots&lt;/id&gt;
       &lt;name&gt;libs-snapshot&lt;/name&gt;
       &lt;url&gt;http://maven.mirror.clvrcld.net:8080/artifactory/libs-snapshot&lt;/url&gt;
      &lt;/repository&gt;
    &lt;/repositories&gt;
    &lt;pluginRepositories&gt;
      &lt;pluginRepository&gt;
       &lt;snapshots&gt;
        &lt;enabled&gt;false&lt;/enabled&gt;
       &lt;/snapshots&gt;
       &lt;id&gt;central&lt;/id&gt;
       &lt;name&gt;plugins-release&lt;/name&gt;
       &lt;url&gt;http://maven.mirror.clvrcld.net:8080/artifactory/plugins-release&lt;/url&gt;
      &lt;/pluginRepository&gt;
      &lt;pluginRepository&gt;
       &lt;snapshots /&gt;
       &lt;id&gt;snapshots&lt;/id&gt;
       &lt;name&gt;plugins-snapshot&lt;/name&gt;
       &lt;url&gt;http://maven.mirror.clvrcld.net:8080/artifactory/plugins-snapshot&lt;/url&gt;
      &lt;/pluginRepository&gt;
    &lt;/pluginRepositories&gt;
    &lt;id&gt;artifactory&lt;/id&gt;
   &lt;/profile&gt;
  &lt;/profiles&gt;
  &lt;activeProfiles&gt;
   &lt;activeProfile&gt;artifactory&lt;/activeProfile&gt;
  &lt;/activeProfiles&gt;
&lt;/settings&gt;
</code></pre>
<p>And that&#39;s it. We just add repositories that will be tried before the user defined repositories. If an artifact is on a private repository, it <strong>will not</strong> be downloaded through nor cached by our artifactory instance.</p>
<h4 id="sbt-client-configuration">SBT client configuration</h4>
<h5 id="add-ivy-typesafe-and-sbt-repositories-to-artifactory">Add ivy typesafe and SBT repositories to Artifactory</h5>
<p>For the SBT instance, there is two things to do: add the ivy typesafe repositories in the Artifactory and configure SBT to use our Artifactory instance.</p>
<p>First, in Artifactory, we add the following remote repositories:</p>
<ul>
<li>sbt-plugin-releases =&gt; <a href="http://repo.scala-sbt.org/scalasbt/sbt-plugin-releases/">http://repo.scala-sbt.org/scalasbt/sbt-plugin-releases/</a></li>
<li>typesafe-ivy-releases =&gt; <a href="http://repo.typesafe.com/typesafe/ivy-releases/">http://repo.typesafe.com/typesafe/ivy-releases/</a></li>
<li>typesafe-maven-releases =&gt; <a href="http://repo.typesafe.com/typesafe/maven-releases/">http://repo.typesafe.com/typesafe/maven-releases/</a></li>
</ul>
<p>Then we put the first two in a new virtual repository (for example <em>ivy-remote-repo</em>), then we add the third one to the <em>remote-repos</em> virtual repository.</p>
<h5 id="configure-sbt">Configure SBT</h5>
<p>Here we come to the core of this post: setting up SBT to use our proxy server while keeping the users out of the trouble of setting a specific Clever Cloud configuring for their applications.</p>
<p>Rummaging through the documentation, we come across a <a href="http://www.scala-sbt.org/release/docs/Detailed-Topics/Proxy-Repositories.html">&quot;Proxy&quot; section</a>.</p>
<p>Let&#39;s follow it and define a ~/.sbt/repositories file:</p>
<pre><code class="language-toml">[repositories]
  local
  maven-local
  clever-ivy-proxy-releases: http://maven.mirror.clvrcld.net:8080/artifactory/ivy-remote-repos, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]
  clever-maven-proxy-releases: http://maven.mirror.clvrcld.net:8080/artifactory/libs-release
  clever-maven-proxy-snapshots: http://maven.mirror.clvrcld.net:8080/artifactory/libs-snapshot
</code></pre>
<p>But <a href="http://instanttrap.com/">it&#39;s a trap</a>! If we had used the <code>-Dsbt.override.build.repos</code> option we would have overriden all the project-defined repositories, even the private ones, which would have led to build failures because of unretrievable dependencies.</p>
<p>So, at this point, no easy configuration was possible. It was either <strong>not</strong> use the <code>~/.sbt/repositories</code> file, either override user-defined repositories.</p>
<p>So let&#39;s be clever, and read about <a href="http://www.scala-sbt.org/release/docs/Detailed-Topics/Global-Settings.html">global settings</a>. We can put a <code>global.sbt</code> file in ~/.sbt/ (and ~/.sbt/0.13/ because of the new 0.13+ global files versioning) that will be used each time we start SBT.</p>
<p>We keep our <code>repositories</code> file, and in the <code>global.sbt</code> file we put the following:</p>
<pre><code class="language-scala">externalResolvers &lt;&lt;= (bootResolvers, externalResolvers) map (
    (boot: Option[Seq[sbt.Resolver]], ext: Seq[sbt.Resolver]) =&gt;
        (boot.getOrElse(Seq.empty[sbt.Resolver]) ++ ext).distinct
)
</code></pre>
<p>Here, the <code>bootResolvers</code> value represents the content of the <code>repositories</code> file, and the <code>externalResolvers</code> value reflects the default SBT repositories plus project-defined repositories.</p>
<p>For curiosity sake, I added some <code>println</code>s in <code>global.sbt</code>:</p>
<pre><code class="language-scala">externalResolvers &lt;&lt;= (bootResolvers, externalResolvers) map (
    (boot: Option[Seq[sbt.Resolver]], ext: Seq[sbt.Resolver]) =&gt; {
        boot.getOrElse(Seq.empty[sbt.Resolver]).foreach(b =&gt; println(&quot;Boot::: &quot; + b.toString))
        ext.foreach(e =&gt; println(&quot;External::: &quot; + e.toString))
        (boot.getOrElse(Seq.empty[sbt.Resolver]) ++ ext).distinct
    }
)
</code></pre>
<p>Then I ran <code>sbt compile</code> in a play project with additional <em>resolvers</em>:</p>
<pre><code class="language-text">...
[info] Set current project to theproject (in build file:/theproject)
Boot::: FileRepository(local,FileConfiguration(true,None),Patterns(ivyPatterns=List(${ivy.home}/local/[organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]), artifactPatterns=List(${ivy.home}/local/[organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]), isMavenCompatible=false))
Boot::: Maven2 Local: file:/home/judu/.m2/repository/
Boot::: URLRepository(clever-ivy-proxy-releases,Patterns(ivyPatterns=List(http://maven.mirror.clvrcld.net:8080/artifactory/ivy-remote-repos/[organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]), artifactPatterns=List(http://maven.mirror.clvrcld.net:8080/artifactory/ivy-remote-repos/[organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]), isMavenCompatible=false))
Boot::: clever-maven-proxy-releases: http://maven.mirror.clvrcld.net:8080/artifactory/libs-release
Boot::: clever-maven-proxy-snapshots: http://maven.mirror.clvrcld.net:8080/artifactory/libs-snapshot
External::: FileRepository(local,FileConfiguration(true,None),Patterns(ivyPatterns=List(${ivy.home}/local/[organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]), artifactPatterns=List(${ivy.home}/local/[organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]), isMavenCompatible=false))
External::: Typesafe Releases Repository: http://repo.typesafe.com/typesafe/releases/
External::: Typesafe Snapshots Repository: http://repo.typesafe.com/typesafe/snapshots/
External::: Couchbase Maven Repository: http://files.couchbase.com/maven2
External::: Local Maven: file:/home/judu/.m2/repository
External::: public: http://repo1.maven.org/maven2/
...
</code></pre>
<p>We can see that bootResolvers contains the resolvers defined in the <code>~/.sbt/repositories</code> file, and externalResolvers contains the default + project defined repositories. As long as SBT will use the repositories in the given order, our proxy repository will be used before the external ones. (Because of the <code>boot ++ ext</code> order.)</p>
<h3 id="sources">Sources</h3>
<ul>
<li><a href="http://blog.dlecan.com/configurer-scala-sbt-repository-artifactory/">http://blog.dlecan.com/configurer-scala-sbt-repository-artifactory/</a> (in french) for the artifactory ivy configuration,</li>
</ul>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
