<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Daniel Wakefield</title>
    <link rel="self" type="application/atom+xml" href="https://danw.xyz/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://danw.xyz"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2024-10-05T00:00:00+00:00</updated>
    <id>https://danw.xyz/atom.xml</id>
    <entry xml:lang="en">
        <title>Metabase tips and tricks</title>
        <published>2024-10-05T00:00:00+00:00</published>
        <updated>2024-10-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://danw.xyz/posts/metabase-tricks/"/>
        <id>https://danw.xyz/posts/metabase-tricks/</id>
        
        <content type="html" xml:base="https://danw.xyz/posts/metabase-tricks/">&lt;h2 id=&quot;flexible-date-bin-ed-dashboards&quot;&gt;flexible date bin&#x27;ed dashboards&lt;&#x2F;h2&gt;
&lt;p&gt;Your consumers want to view the same data over different timeframes
You want to build a dashboard once to prevent definition drifts.&lt;&#x2F;p&gt;
&lt;p&gt;E.g Total Payments by Day, Total Payments by Month, etc&lt;&#x2F;p&gt;
&lt;p&gt;We handle this with a few tricks that the data consumers understand without issue.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;helper-model&quot;&gt;Helper Model&lt;&#x2F;h3&gt;
&lt;p&gt;We defined a &lt;a href=&quot;https:&#x2F;&#x2F;www.metabase.com&#x2F;docs&#x2F;latest&#x2F;data-modeling&#x2F;models&quot;&gt;Helper Model&lt;&#x2F;a&gt; with the time periods we offer for analytics.
We&#x27;re lucky that we don&#x27;t need super granular stats and can just offer down to &lt;code&gt;hour&lt;&#x2F;code&gt;.
You could have custom periods but you will need to replace &lt;code&gt;date_trunc&lt;&#x2F;code&gt; with &lt;code&gt;date_bin&lt;&#x2F;code&gt; in the examples below and take care to read caveat 1.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;SELECT
&lt;&#x2F;span&gt;&lt;span&gt;    time_period
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; (
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;VALUES
&lt;&#x2F;span&gt;&lt;span&gt;    (&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hour&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;),
&lt;&#x2F;span&gt;&lt;span&gt;    (&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;day&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;),
&lt;&#x2F;span&gt;&lt;span&gt;    (&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;week&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;),
&lt;&#x2F;span&gt;&lt;span&gt;    (&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;month&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;),
&lt;&#x2F;span&gt;&lt;span&gt;    (&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;quarter&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;),
&lt;&#x2F;span&gt;&lt;span&gt;    (&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;year&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;),
&lt;&#x2F;span&gt;&lt;span&gt;  ) t(time_period)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;filtering-the-result-set&quot;&gt;Filtering the result set&lt;&#x2F;h3&gt;
&lt;p&gt;In any query that requires the ability to date bin we use one of the following as part of the query definition&lt;&#x2F;p&gt;
&lt;h4 id=&quot;use-alongside-date-field-filter&quot;&gt;Use alongside date field filter&lt;&#x2F;h4&gt;
&lt;p&gt;This works great for simple queries where you are filtering on a single date field as the date field filter has a bunch of
useful settings like inclusive&#x2F;exclusive end, ability to exclude days of the week etc that are often requested by our consumers&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;SELECT
&lt;&#x2F;span&gt;&lt;span&gt;  date_trunc({{time_period}}, created_at)::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;date &lt;&#x2F;span&gt;&lt;span&gt;AS time_period,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;SUM&lt;&#x2F;span&gt;&lt;span&gt;(amount)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; payments
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;WHERE
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;  [[AND {{payment_created_at_field_filter}}]]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;GROUP BY&lt;&#x2F;span&gt;&lt;span&gt; time_period
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;when-you-can-t-use-the-field-filter&quot;&gt;When you can&#x27;t use the field filter&lt;&#x2F;h4&gt;
&lt;p&gt;Metabase doesnt support date field filters on thee virtual tables created by CTE&#x27;s, and there are times where you want to filter multiple tables to the same time range.&lt;&#x2F;p&gt;
&lt;p&gt;In those cases we add a &lt;code&gt;number of periods&lt;&#x2F;code&gt; filter that defines the how far to look back over instead&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;SELECT
&lt;&#x2F;span&gt;&lt;span&gt;  date_trunc({{time_period}}, created_at)::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;date &lt;&#x2F;span&gt;&lt;span&gt;as time_period,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;(amount)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; payments
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;JOIN&lt;&#x2F;span&gt;&lt;span&gt; users on &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;payments&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;user_id &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;users&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;id
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;WHERE
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;payments&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;created_at &lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;= date_trunc({{time_period}}, NOW() - ({{num_periods}} || &amp;#39; &amp;#39; || {{time_period}})::interval)
&lt;&#x2F;span&gt;&lt;span&gt;  AND
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;users&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;last_sign_in_at &lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;= date_trunc({{time_period}}, NOW() - ({{num_periods}} || &amp;#39; &amp;#39; || {{time_period}})::interval)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;GROUP BY&lt;&#x2F;span&gt;&lt;span&gt; time_period
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h5 id=&quot;caveat-1-quarter&quot;&gt;Caveat 1: Quarter&lt;&#x2F;h5&gt;
&lt;p&gt;&lt;code&gt;date_trunc&lt;&#x2F;code&gt; works with &lt;code&gt;quarter&lt;&#x2F;code&gt; but &lt;code&gt;interval&lt;&#x2F;code&gt; doesnt.
It would be a lovely addition&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#quarter&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; but for now we add a case statement that turns 1 quarter into 3 months. We&#x27;re Ok with a slight inaccuracy here.
Use a similar technique in you have custom time_periods.
You need to do some math to make the multiplication understandable. e.g &lt;code&gt;num_periods = 4 &lt;&#x2F;code&gt; &amp;amp; &lt;code&gt;time_period = &#x27;15 minutes&#x27;&lt;&#x2F;code&gt; you want to multiple by 15 before converting to an interval.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;payments&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;created_at &lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;= date_trunc(
&lt;&#x2F;span&gt;&lt;span&gt;  {{time_period}},
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;CASE&lt;&#x2F;span&gt;&lt;span&gt; {{time_period}}
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;WHEN &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;quarter&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;THEN
&lt;&#x2F;span&gt;&lt;span&gt;      date_trunc(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;quarter&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, NOW()) - ((({{num_periods}})::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;integer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;) || &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; month&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;)::interval
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;ELSE
&lt;&#x2F;span&gt;&lt;span&gt;      NOW() - ({{num_periods}} || &amp;#39; &amp;#39; || {{time_period}})::interval
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;END
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;danw.xyz&#x2F;posts&#x2F;metabase-tricks&#x2F;mbquarter.png&quot; alt=&quot;Image&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h5 id=&quot;caveat-2-incomplete-periods&quot;&gt;Caveat 2: Incomplete periods&lt;&#x2F;h5&gt;
&lt;p&gt;We include data from the incomplete current period but we can change this by adding a less than version to complement each
filter&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;SELECT
&lt;&#x2F;span&gt;&lt;span&gt;  date_trunc({{time_period}}, created_at)::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;date &lt;&#x2F;span&gt;&lt;span&gt;AS time_period,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;SUM&lt;&#x2F;span&gt;&lt;span&gt;(amount)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; payments
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;payments&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;created_at &lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;= date_trunc({{time_period}}, NOW() - ({{num_periods}} || &amp;#39; &amp;#39; || {{time_period}})::interval)
&lt;&#x2F;span&gt;&lt;span&gt;  AND &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;payments&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;created_at &lt;&#x2F;span&gt;&lt;span&gt;&amp;lt; date_trunc({{time_period}}, NOW())
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The majority of our data analysis is done on historic data, but of course you can:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Flip the signs to only look forward&lt;&#x2F;li&gt;
&lt;li&gt;Use it with between and &lt;code&gt;NOW() + num_periods&lt;&#x2F;code&gt; &amp;amp; &lt;code&gt;NOW() - num_periods&lt;&#x2F;code&gt; to look equally in both directions&lt;&#x2F;li&gt;
&lt;li&gt;Define both &lt;code&gt;backward_periods&lt;&#x2F;code&gt; &amp;amp; &lt;code&gt;forward_periods&lt;&#x2F;code&gt; for use in the above if they must be unique&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;optional-targets&quot;&gt;Optional targets&lt;&#x2F;h3&gt;
&lt;p&gt;You&#x27;ll probably have a tracking board, daily or monthly that the leadership team love to watch.
They will also want to compare past periods to the current one without going to a new dashboard. Eg (looking at today vs the big 1 day sale last week)
Combining &lt;code&gt;COALESCE&lt;&#x2F;code&gt; with optional filters and you can inject SQL defaults.&lt;&#x2F;p&gt;
&lt;p&gt;This gets around the fact that raw date filters can&#x27;t be set to &lt;code&gt;today&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Something like this works&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt; filtered_payments as (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;* some query *&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;SELECT
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;COUNT&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; filtered_payments
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;WHERE
&lt;&#x2F;span&gt;&lt;span&gt;  date_trunc(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;day&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;filtered_payments&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;created_at&lt;&#x2F;span&gt;&lt;span&gt;) = date_trunc(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;day&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, COALESCE( [[{{date_to_look_at}} ,]] NOW() ))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;percent-function&quot;&gt;Percent function&lt;&#x2F;h3&gt;
&lt;p&gt;To remove some error prone casting and coalesce&#x27;ing related to calculating
percentages, especially over counts, we added a function to make it more
readable&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;CREATE OR REPLACE FUNCTION &lt;&#x2F;span&gt;&lt;span&gt;public.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;percent&lt;&#x2F;span&gt;&lt;span&gt;(portion &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;numeric&lt;&#x2F;span&gt;&lt;span&gt;, total &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;numeric&lt;&#x2F;span&gt;&lt;span&gt;, round_level &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;integer DEFAULT &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  RETURNS &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;numeric
&lt;&#x2F;span&gt;&lt;span&gt;  LANGUAGE sql
&lt;&#x2F;span&gt;&lt;span&gt;  IMMUTABLE PARALLEL SAFE STRICT
&lt;&#x2F;span&gt;&lt;span&gt;AS $function$
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;select
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;CASE
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;WHEN&lt;&#x2F;span&gt;&lt;span&gt; total = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;THEN &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;ELSE&lt;&#x2F;span&gt;&lt;span&gt; ROUND(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;100 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; (portion &#x2F; total), round_level)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;END
&lt;&#x2F;span&gt;&lt;span&gt;$function$
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It replaces the following, while also handling counts that return &lt;code&gt;0&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;SELECT
&lt;&#x2F;span&gt;&lt;span&gt;  ROUND(100 * cast(count(*) FILTER (where amount &amp;gt; 100) as float)&#x2F;cast(count(*) as float)) AS old_big_payment_rate,
&lt;&#x2F;span&gt;&lt;span&gt;  percent(
&lt;&#x2F;span&gt;&lt;span&gt;    COUNT(*) FILTER (where amount &amp;gt; 100),
&lt;&#x2F;span&gt;&lt;span&gt;    COUNT(*)
&lt;&#x2F;span&gt;&lt;span&gt;  ) AS new_big_payment_rate
&lt;&#x2F;span&gt;&lt;span&gt;FROM payments
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;commonly-accessed-questions&quot;&gt;Commonly accessed questions&lt;&#x2F;h3&gt;
&lt;p&gt;We do periodic cleaning of the available dashboards; we&#x27;ll archive things that havent been accessed in the last 6-12 months to keep the number of questions and dashboards down.
The below will give you the commonly accessed queries, flipping or changing the order to last_execution will show you the ones that aren&#x27;t often accessed.
Use an Anti-Join to find the reports that haven&#x27;t been run at all in the period you&#x27;re checking.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;with card_data as (
&lt;&#x2F;span&gt;&lt;span&gt;  select
&lt;&#x2F;span&gt;&lt;span&gt;    card_id,
&lt;&#x2F;span&gt;&lt;span&gt;    COUNT(*) as execution_count,
&lt;&#x2F;span&gt;&lt;span&gt;    MAX(started_at) as last_execution
&lt;&#x2F;span&gt;&lt;span&gt;  from query_execution
&lt;&#x2F;span&gt;&lt;span&gt;  where
&lt;&#x2F;span&gt;&lt;span&gt;    started_at &amp;gt; NOW() - &amp;#39;12 months&amp;#39;::interval
&lt;&#x2F;span&gt;&lt;span&gt;  GROUP BY 1
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;select
&lt;&#x2F;span&gt;&lt;span&gt;  report_card.id,
&lt;&#x2F;span&gt;&lt;span&gt;  report_card.name,
&lt;&#x2F;span&gt;&lt;span&gt;  CONCAT(&amp;#39;https:&#x2F;&#x2F;data.example.com&#x2F;questions&#x2F;&amp;#39;, report_card.id),
&lt;&#x2F;span&gt;&lt;span&gt;  card_data.execution_count,
&lt;&#x2F;span&gt;&lt;span&gt;  card_data.last_execution
&lt;&#x2F;span&gt;&lt;span&gt;from report_card
&lt;&#x2F;span&gt;&lt;span&gt;join card_data on card_data.card_id = report_card.id
&lt;&#x2F;span&gt;&lt;span&gt;where
&lt;&#x2F;span&gt;&lt;span&gt;  report_card.archived = false
&lt;&#x2F;span&gt;&lt;span&gt;ORDER BY card_data.execution_count DESC
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;quarter&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.postgresql.org&#x2F;message-id&#x2F;6b2a4293-6f88-8114-aa0f-d7becdaafbdf%40cam.ac.uk&quot;&gt;Wish: support &quot;quarter&quot; in Interval&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Using certificate based ssh authentication</title>
        <published>2014-09-20T00:00:00+00:00</published>
        <updated>2014-09-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://danw.xyz/posts/cert-authentication/"/>
        <id>https://danw.xyz/posts/cert-authentication/</id>
        
        <content type="html" xml:base="https://danw.xyz/posts/cert-authentication/">&lt;h2 id=&quot;what-is-a-certificate&quot;&gt;What is a Certificate?&lt;&#x2F;h2&gt;
&lt;p&gt;A certificate is a form of &lt;a href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Public-key_cryptography&quot;&gt;Public Key Cryptography&lt;&#x2F;a&gt; that allows you to trust someone.&lt;&#x2F;p&gt;
&lt;p&gt;The certificate authority&#x27;s (CA) private key is used to sign
the public key of anyone or anything that is trusted by the CA.&lt;&#x2F;p&gt;
&lt;p&gt;Anybody can then decide to trust the same people as the CA by
adding the CA&#x27;s public key to their keyring.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-should-i-use-one&quot;&gt;Why should I use one?&lt;&#x2F;h2&gt;
&lt;p&gt;One of the main reasons is that CA signing works in both directions.&lt;&#x2F;p&gt;
&lt;p&gt;A user can check that a machine is trusted instead of relying
on adding individual hosts to &lt;code&gt;~&#x2F;.ssh&#x2F;known_hosts&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This is useful if you are using the same IP address but you are recreating
a machine for testing purposes and you don&#x27;t want to be bugged by SSH key
checking messages.&lt;&#x2F;p&gt;
&lt;p&gt;A machine can check that a user is trusted instead of relying on adding
user&#x27;s public key&#x27;s to &lt;code&gt;~&#x2F;.ssh&#x2F;authorized_keys&lt;&#x2F;code&gt; for each user that
should be allowed to login.&lt;&#x2F;p&gt;
&lt;p&gt;Signing keys also allows very fine grained control over many aspects
that key based authentication doesn&#x27;t.&lt;&#x2F;p&gt;
&lt;p&gt;The reason I have switched from key based to cert based authentication
is that it is much easier to handle access controls access your personal
machines when you are regularly creating new VM&#x27;s and VPS&#x27;s.&lt;&#x2F;p&gt;
&lt;p&gt;You only need to add the CA&#x27;s public key to a server rather than
all of your user&#x27;s public keys to the correct &lt;code&gt;~&#x2F;.ssh&#x2F;authorized_key&lt;&#x2F;code&gt;s file.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-it-up&quot;&gt;Setting it up.&lt;&#x2F;h2&gt;
&lt;p&gt;For this you will need:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;2+ machines running your preferred form of Linux (I used Debian 7)&lt;&#x2F;li&gt;
&lt;li&gt;Openssh 5.4+&lt;&#x2F;li&gt;
&lt;li&gt;Basic knowledge of the command line&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In this example I will be using the machine I am writing this on as both the
CA and the user I want to trust.
This isn&#x27;t totally secure but will work perfectly fine with a small number of users.&lt;&#x2F;p&gt;
&lt;p&gt;First we will create a user CA which will allow the users to sign into servers without a prompt.&lt;&#x2F;p&gt;
&lt;p&gt;The first step is to create your CA&#x27;s keypair.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; mkdir&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -p ~&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;.ssh&#x2F;ca
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; cd &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;~&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;.ssh&#x2F;ca
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ssh-keygen&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -f&lt;&#x2F;span&gt;&lt;span&gt; ca&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -C &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Comment on the keys&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Generating&lt;&#x2F;span&gt;&lt;span&gt; public&#x2F;private rsa key pair.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Enter&lt;&#x2F;span&gt;&lt;span&gt; passphrase (empty for no passphrase)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Enter&lt;&#x2F;span&gt;&lt;span&gt; same passphrase again:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Your&lt;&#x2F;span&gt;&lt;span&gt; identification has been saved in ca.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Your&lt;&#x2F;span&gt;&lt;span&gt; public key has been saved in ca.pub.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ls
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ca&lt;&#x2F;span&gt;&lt;span&gt;  ca.pub
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You should provide a strong passphrase when asked as this will mean
that if attacker gains access to your keys your security is not
compromised.&lt;&#x2F;p&gt;
&lt;p&gt;The next step is to place the public key on the machine(s) that should
trust the users.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; scp ca.pub user@example.com:&#x2F;home&#x2F;user
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ssh user@example.com &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;sudo cp ~&#x2F;ca.pub &#x2F;etc&#x2F;ssh&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ssh user@example.com &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;sudo sed -i &amp;#39;&#x2F;TrustedUserCAKeys&#x2F;d&amp;#39; &#x2F;etc&#x2F;ssh&#x2F;sshd_config&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ssh user@example.com &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;echo &amp;#39;TrustedUserCAKeys &#x2F;etc&#x2F;ssh&#x2F;ca.pub&amp;#39; | sudo tee -a &#x2F;etc&#x2F;ssh&#x2F;sshd_config&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ssh user@example.com &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;sudo &#x2F;etc&#x2F;init.d&#x2F;ssh restart&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# The above will only work on a machine set up with passwordless sudo
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Otherwise ssh to the machine and run the commands manually
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This sets up the server to trust signed keys from the CA, so the next step
is to sign our users public key.&lt;&#x2F;p&gt;
&lt;p&gt;If you already have a set of user keys you should skip the first step.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ssh-keygen
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Generating&lt;&#x2F;span&gt;&lt;span&gt; public&#x2F;private rsa key pair.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Enter&lt;&#x2F;span&gt;&lt;span&gt; passphrase (empty for no passphrase)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Enter&lt;&#x2F;span&gt;&lt;span&gt; same passphrase again:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Your&lt;&#x2F;span&gt;&lt;span&gt; identification has been saved in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;~&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;.ssh&#x2F;id_rsa.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Your&lt;&#x2F;span&gt;&lt;span&gt; public key has been saved in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;~&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;.ssh&#x2F;id_rsa.pub
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# This generates a keypair with default settings
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Like with the CA keypair you should use a strong password for
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# added security.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; mkdir signed_users
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; cd signed_users
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; cp &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;~&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;.ssh&#x2F;id_rsa.pub user.pub
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ssh-keygen&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -s ~&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;.ssh&#x2F;ca&#x2F;ca&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -I &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Comment&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -n &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; user.pub
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Enter&lt;&#x2F;span&gt;&lt;span&gt; passphrase:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Enter the passphrase you used to create the ca keypair
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# You can add more than one user by comma separating them
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# You will only be allowed to login as the users specified
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# in this list and it is required
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; chmod 600 user-cert.pub
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Set the correct permissions on your signed key
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ls
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;user.pub&lt;&#x2F;span&gt;&lt;span&gt;    user-cert.pub
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; cp user-cert.pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;~&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;.ssh&#x2F;id_rsa-cert.pub
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You should keep a copy of &lt;em&gt;user&lt;&#x2F;em&gt;.pub safe because if you ever
want to revoke a users trusted status you need
to know their public key.&lt;&#x2F;p&gt;
&lt;p&gt;You can easily repeat this for each machine you use regularly.&lt;&#x2F;p&gt;
&lt;p&gt;If you use an &lt;a href=&quot;http:&#x2F;&#x2F;www.ansible.com&quot;&gt;automation tool&lt;&#x2F;a&gt;
to set up new machines it is simple to set up a task that
will create and sign a keypair allowing you to log in to any
of your machines from it straight away.&lt;&#x2F;p&gt;
&lt;p&gt;There are many &lt;a href=&quot;http:&#x2F;&#x2F;www.openbsd.org&#x2F;cgi-bin&#x2F;man.cgi&#x2F;OpenBSD-current&#x2F;man1&#x2F;ssh-keygen.1&quot;&gt;other options&lt;&#x2F;a&gt; you can add to the signed key to restrict and secure the user further.&lt;&#x2F;p&gt;
&lt;p&gt;Some examples are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Setting an expiration on the signed key&lt;&#x2F;li&gt;
&lt;li&gt;Only allowing the key to be used from certain address&#x27;s&lt;&#x2F;li&gt;
&lt;li&gt;Forcing a specific command to be run instead of a shell when using the certificate&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;revoking-users&quot;&gt;Revoking users&lt;&#x2F;h2&gt;
&lt;p&gt;If you lose access to a set of user keys it is fairly easy to revoke their
access to your machines.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ssh user@example.com &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[ -f &#x2F;etc&#x2F;ssh&#x2F;ssh_revoked_keys ] || sudo install -m 644 -o root -g root &#x2F;etc&#x2F;ssh&#x2F;ssh_revoked_keys&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; cat user_to_revoke.pub | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ssh&lt;&#x2F;span&gt;&lt;span&gt; user@example.com &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;sudo tee -a &#x2F;etc&#x2F;ssh&#x2F;ssh_revoked_keys&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ssh user@example.com &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;sudo sed -i &amp;#39;&#x2F;RevokedKeys&#x2F;d&amp;#39; &#x2F;etc&#x2F;ssh&#x2F;sshd_config&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ssh user@example.com &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;echo &amp;#39;RevokedKeys &#x2F;etc&#x2F;ssh&#x2F;ssh_revoked_keys&amp;#39; | sudo tee -a &#x2F;etc&#x2F;ssh&#x2F;sshd_config&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ssh user@example.com &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;sudo &#x2F;etc&#x2F;init.d&#x2F;ssh restart&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Again this requires passwordless sudo
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This does have to be run on each of your machines but it is still less work
than combing through authorized_keys files for each user account on every
machine you have.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;final-notes&quot;&gt;Final notes.&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Keep you CA private key safe.&lt;br &#x2F;&gt;
This can be used to sign new keys that can then access your machines.&lt;&#x2F;li&gt;
&lt;li&gt;Use passphrase&#x27;s and a SSH agent.&lt;br &#x2F;&gt;
These improve security so that an attacker must have access to a key and
know the passphrase before using it. Using an SSH agent means you only
have to input your passphrase once across machine reboots.&lt;&#x2F;li&gt;&lt;&#x2F;li&gt;
&lt;li&gt;You should try to follow the &lt;a href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Principle_of_least_privilege&quot;&gt;principle of least privilege&lt;&#x2F;a&gt;
for user accounts and give each one their own signed key.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
</feed>
