<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.0">Jekyll</generator><link href="http://leethax.org/feed.xml" rel="self" type="application/atom+xml" /><link href="http://leethax.org/" rel="alternate" type="text/html" /><updated>2022-01-19T13:03:49+00:00</updated><id>http://leethax.org/feed.xml</id><title type="html">leethax.org</title><subtitle>Collection of snippets or oneliners that made my life easier ;)</subtitle><entry><title type="html">Compiling PHP SQLite extension from source with encryption features</title><link href="http://leethax.org/2020/02/06/compiling-php-sqlite-extension-with-encryption.html" rel="alternate" type="text/html" title="Compiling PHP SQLite extension from source with encryption features" /><published>2020-02-06T14:13:00+00:00</published><updated>2020-02-06T14:13:00+00:00</updated><id>http://leethax.org/2020/02/06/compiling-php-sqlite-extension-with-encryption</id><content type="html" xml:base="http://leethax.org/2020/02/06/compiling-php-sqlite-extension-with-encryption.html">&lt;p&gt;I am writing this post because, as often this is not documented anywhere, but if you purchased a &lt;a href=&quot;https://sqlite.org/see/doc/release/www/index.wiki&quot;&gt;SQLite Encryption Extension&lt;/a&gt; (SEE) License and want to use it within PHP, you will have to recompile the PHP SQLite3 extension from source.&lt;/p&gt;

&lt;h1 id=&quot;what-you-will-need&quot;&gt;What you will need&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;PHP source code for your current version of PHP, found on https://www.php.net/downloads.php&lt;/li&gt;
  &lt;li&gt;SEE source code found on the version above&lt;/li&gt;
  &lt;li&gt;php-dev tooling for your distro, (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt install php-dev&lt;/code&gt; for Debian/Ubuntu)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;prepare-the-source-dirs&quot;&gt;Prepare the source dirs&lt;/h1&gt;
&lt;p&gt;I am assuming php 7.2.27 here but correct with the version that you’re using.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mkdir ~/src
tar zxf see-sources.tar.gz -C src
tar zxf php-7.2.27.tar.gz -C src
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;prepare-see-source&quot;&gt;Prepare SEE source&lt;/h1&gt;
&lt;p&gt;First of all the SQLite amalgamation with SEE needs to be prepared, go to your SEE source repository and issue the following:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cd ~/src/see-sources/
cat see-prefix.txt sqlite3.c see.c &amp;gt;sqlite3-see.c
cp sqlite3-see.c ~/src/php-7.2.27/src/ext/sqlite3/libsqlite/sqlite3.c
cp sqlite3.h ~/src/php-7.2.27/ext/sqlite3/libsqlite/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You have now successfully replaced the bundled sqlite3 library in the PHP source by the one with encryption features. All what’s left to do is compile it, and replace it in the running PHP. But before that…&lt;/p&gt;

&lt;h1 id=&quot;compile-the-sqlite3-extension&quot;&gt;Compile the SQLite3 extension&lt;/h1&gt;
&lt;p&gt;Compiling the PHP extension is easy, but what the documentation does not say is that a flag needs to be enabled in order for encryption to be loaded. Also, the m4 file has the wrong name for some reason and must be renamed first:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mv config0.m4 config.m4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.m4&lt;/code&gt; with your favorite editor, and add this to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;other_flags&lt;/code&gt; variable (line 81 in php 7.2, could be another location depending on version):&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;     other_flags=&quot;-DSQLITE_HAS_CODEC=1 -DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_FTS4=1 -DSQLITE_ENABLE_FTS5=1 -DSQLITE_CORE=1 -DSQLITE_ENABLE_COLUMN_METADATA=1&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now it is ready to compile so just issue the regular commands:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;phpize
./configure
make
make install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You should get the following output if all goes well:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Configuring for:
PHP Api Version:         20170718
Zend Module Api No:      20170718
Zend Extension Api No:   320170718

Installing shared extensions:     /usr/lib/php/20170718/
Installing header files:          /usr/include/php/20170718/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Voila! Your extension should be installed in the correct location. Don’t forget to reload PHP-FPM or Apache if you’re using PHP on the server side.&lt;/p&gt;</content><author><name>tanji</name></author><category term="php" /><category term="sqlite" /><category term="encryption" /><category term="extension" /><category term="compile" /><category term="source" /><summary type="html">I am writing this post because, as often this is not documented anywhere, but if you purchased a SQLite Encryption Extension (SEE) License and want to use it within PHP, you will have to recompile the PHP SQLite3 extension from source.</summary></entry><entry><title type="html">Golang testing with sqlmock and gorm</title><link href="http://leethax.org/2019/08/28/golang-sqlmock-gorm.html" rel="alternate" type="text/html" title="Golang testing with sqlmock and gorm" /><published>2019-08-28T15:46:00+00:00</published><updated>2019-08-28T15:46:00+00:00</updated><id>http://leethax.org/2019/08/28/golang-sqlmock-gorm</id><content type="html" xml:base="http://leethax.org/2019/08/28/golang-sqlmock-gorm.html">&lt;p&gt;While writing tests for a Go project I needed a way to test my database queries. For this the library &lt;a href=&quot;https://github.com/DATA-DOG/go-sqlmock&quot;&gt;sqlmock&lt;/a&gt; exists. This works great and also does what I wanted. However a problem arose that it seemed to share the connection across mocks even though they were re-init’d at the start of the test. This was due to me not implicitly calling a DSN for the sqlmock.&lt;/p&gt;

&lt;p&gt;Before I had the following:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;does not work as intended&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// we only need the mock interface&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlmock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// open the database for gorm (*gorm.DB)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;mockedGorm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gorm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sqlmock&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;sqlmock_db_0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// init your own database interface&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dbInterface&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewDatabase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// set the *gorm.DB on your interface&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dbInterface&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Conn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mockedGorm&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// close the DB when the function is over&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mockedGorm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This did work, however in running the second test, it complained the connection was closed. Weird because I called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlmock.New()&lt;/code&gt; in each test. Turns out because you provide a DSN to Gorm you need to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlmock.NewWithDSN(&quot;some_dsn_string&quot;)&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;does work&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// we only need the mock interface&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// explicitely set the DSN&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlmock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewWithDSN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sqlmock_db_0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// open the database for gorm (*gorm.DB)&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// ensure the DSN matches.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;mockedGorm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gorm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sqlmock&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;sqlmock_db_0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// init your own database interface&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dbInterface&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewDatabase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// set the *gorm.DB on your interface&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dbInterface&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Conn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mockedGorm&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// close the DB when the function is over&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mockedGorm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What is important is that you &lt;em&gt;do not&lt;/em&gt; share the DSN across your test functions as it’ll keep sharing the stubbed DB conn.&lt;/p&gt;

&lt;p&gt;For easier tests and preventing to initiate this each time we can do the following:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;dsn_count&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provideMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqlmock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sqlmock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gorm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DispatchDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;dsn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sprintf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sqlmock_db_%d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dsnCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlmock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewWithDSN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dsn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;panic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;mockedGorm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gorm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sqlmock&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dsn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;panic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;dbInterface&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewDatabase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;dbInterface&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Conn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mockedGorm&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;dsnCount&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mockedGorm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbInterface&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TestMain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;dsnCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Test_someFunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mockedGorm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbInterface&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provideMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mockedGorm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

	&lt;span class=&quot;c&quot;&gt;// your tests using the mock&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>terwey</name></author><category term="golang" /><category term="go" /><category term="sqlmock" /><category term="gorm" /><category term="test" /><summary type="html">While writing tests for a Go project I needed a way to test my database queries. For this the library sqlmock exists. This works great and also does what I wanted. However a problem arose that it seemed to share the connection across mocks even though they were re-init’d at the start of the test. This was due to me not implicitly calling a DSN for the sqlmock.</summary></entry><entry><title type="html">MySQL SHOW TABLES and information_schema privileges</title><link href="http://leethax.org/2019/08/23/mysql-information-schema-privileges.html" rel="alternate" type="text/html" title="MySQL SHOW TABLES and information_schema privileges" /><published>2019-08-23T06:45:00+00:00</published><updated>2019-08-23T06:45:00+00:00</updated><id>http://leethax.org/2019/08/23/mysql-information-schema-privileges</id><content type="html" xml:base="http://leethax.org/2019/08/23/mysql-information-schema-privileges.html">&lt;p&gt;This subject came up when writing monitoring scripts: How do you give privileges to a MySQL or MariaDB user, so it will be able to access the table metadata without accessing the data?&lt;/p&gt;

&lt;p&gt;As a refresher, MySQL schema and table metadata is accessible in the ISO-standard set of views contained in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;information_schema&lt;/code&gt; database.
It can also be found at a more limited level with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHOW TABLES&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHOW TABLE STATUS&lt;/code&gt; commands.&lt;/p&gt;

&lt;p&gt;Unfortunately, turns out there are no metadata privileges for such needs. The rule is the following:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Each MySQL user has the right to access these tables, but can see only the rows in the tables that correspond to objects for which the user has the proper access privileges. In some cases (for example, the ROUTINE_DEFINITION column in the INFORMATION_SCHEMA ROUTINES table), users who have insufficient privileges see NULL. These restrictions do not apply for InnoDB tables; you can see them with only the PROCESS privilege.
The same privileges apply to selecting information from INFORMATION_SCHEMA and viewing the same information through SHOW statements. In either case, you must have some privilege on an object to see information about it.
© 2019, Oracle Corporation and/or its affiliates&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That means the user must be granted some kind of privilege on the destination object, or it won’t be able to read any metadata. The issue here, is that we don’t want to give our user any kind of potentially sensitive or destructive privilege (i.e. nothing in the SELECT/INSERT/UPDATE/DELETE range).&lt;/p&gt;

&lt;p&gt;A quick call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHOW PRIVILEGES&lt;/code&gt; gives us the following overview:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+-------------------------+---------------------------------------+-------------------------------------------------------+
| Privilege               | Context                               | Comment                                               |
+-------------------------+---------------------------------------+-------------------------------------------------------+
| Alter                   | Tables                                | To alter the table                                    |
| Alter routine           | Functions,Procedures                  | To alter or drop stored functions/procedures          |
| Create                  | Databases,Tables,Indexes              | To create new databases and tables                    |
| Create routine          | Databases                             | To use CREATE FUNCTION/PROCEDURE                      |
| Create temporary tables | Databases                             | To use CREATE TEMPORARY TABLE                         |
| Create view             | Tables                                | To create new views                                   |
| Create user             | Server Admin                          | To create new users                                   |
| Delete                  | Tables                                | To delete existing rows                               |
| Drop                    | Databases,Tables                      | To drop databases, tables, and views                  |
| Event                   | Server Admin                          | To create, alter, drop and execute events             |
| Execute                 | Functions,Procedures                  | To execute stored routines                            |
| File                    | File access on server                 | To read and write files on the server                 |
| Grant option            | Databases,Tables,Functions,Procedures | To give to other users those privileges you possess   |
| Index                   | Tables                                | To create or drop indexes                             |
| Insert                  | Tables                                | To insert data into tables                            |
| Lock tables             | Databases                             | To use LOCK TABLES (together with SELECT privilege)   |
| Process                 | Server Admin                          | To view the plain text of currently executing queries |
| Proxy                   | Server Admin                          | To make proxy user possible                           |
| References              | Databases,Tables                      | To have references on tables                          |
| Reload                  | Server Admin                          | To reload or refresh tables, logs and privileges      |
| Replication client      | Server Admin                          | To ask where the slave or master servers are          |
| Replication slave       | Server Admin                          | To read binary log events from the master             |
| Select                  | Tables                                | To retrieve rows from table                           |
| Show databases          | Server Admin                          | To see all databases with SHOW DATABASES              |
| Show view               | Tables                                | To see views with SHOW CREATE VIEW                    |
| Shutdown                | Server Admin                          | To shut down the server                               |
| Super                   | Server Admin                          | To use KILL thread, SET GLOBAL, CHANGE MASTER, etc.   |
| Trigger                 | Tables                                | To use triggers                                       |
| Create tablespace       | Server Admin                          | To create/alter/drop tablespaces                      |
| Update                  | Tables                                | To update existing rows                               |
| Usage                   | Server Admin                          | No privileges - allow connect only                    |
+-------------------------+---------------------------------------+-------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We just need to pick up a privilege in the &lt;em&gt;Tables&lt;/em&gt; context for our monitoring user to be able to read metadata. All things considered, the &lt;em&gt;References&lt;/em&gt; seems to be the less destructive - it only allows a user to create foreign keys on a table. &lt;em&gt;Create&lt;/em&gt; could also be used if you don’t fear being spammed with useless tables.&lt;/p&gt;

&lt;p&gt;To implement that we just need to grant the chosen privilege to our monitoring user. It will now be able to access object metadata in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;information_schema&lt;/code&gt; views.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;grant references on backenddb.* TO 'monitoring_user'@'%';

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>tanji</name></author><category term="mysql" /><category term="mariadb" /><category term="metadata" /><category term="privileges" /><summary type="html">This subject came up when writing monitoring scripts: How do you give privileges to a MySQL or MariaDB user, so it will be able to access the table metadata without accessing the data?</summary></entry><entry><title type="html">Rundeck accepting incoming webhooks from e.g. Github</title><link href="http://leethax.org/2019/05/18/rundeck-github-webhook.html" rel="alternate" type="text/html" title="Rundeck accepting incoming webhooks from e.g. Github" /><published>2019-05-18T22:30:00+00:00</published><updated>2019-05-18T22:30:00+00:00</updated><id>http://leethax.org/2019/05/18/rundeck-github-webhook</id><content type="html" xml:base="http://leethax.org/2019/05/18/rundeck-github-webhook.html">&lt;p&gt;We wanted to start using Rundeck more to replace an aging Jenkins system. However one thing was really missing from Rundeck, which was the ability to accept an incoming Webhook from Github.
This boggled my mind for a bit, why isn’t this simply accepted. If we have a parameter called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;payload&lt;/code&gt; we’re good to go as a POST right. All I needed was something that could convert between the POST that Github sends as a Webhook and what Rundeck wants through their API.&lt;/p&gt;

&lt;p&gt;I decided to solve this using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx-njs&lt;/code&gt; which is a Javascript implementation inside nginx instead of using for example Lua.&lt;/p&gt;

&lt;p&gt;What this eventually allows you to do is configure Github to use an URL like the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://user:pass@rundeck.yourcool.domain/build/5fdf3803-d19d-4436-91d3-2d700904dbfb

# or using a name you mapped

https://user:pass@rundeck.yourcool.domain/buildByName/mycooljob
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I hope this was helpful for someone trying to solve the same problem.&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;
&lt;p&gt;You’ll need nginx with njs support, their upstream repo’s for Debian have this out-of-the box. &lt;a href=&quot;http://nginx.org/en/linux_packages.html&quot;&gt;See here for how to get the repository setup&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apt-get install nginx nginx-module-njs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;nginx&quot;&gt;nginx&lt;/h2&gt;

&lt;p&gt;The following changes need to be made to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/nginx/nginx.conf&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;load_module modules/ngx_http_js_module.so;
load_module modules/ngx_stream_js_module.so;

http {
    js_include /etc/nginx/webhook.js;
    js_set $webhook payload_convert;
    js_set $jobid getJobID;
    js_set $jobUUIDFromName getBuildByName;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And we need the vhost config, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/nginx/conf.d/rundeck.conf&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;server {
    listen 80 default_server; 
    listen [::]:80 default_server;

    # Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
    return 301 https://$host$request_uri;
}


server {
    listen       443 ssl;
    server_name  rundeck.yourcool.domain;

    #ssl_certificate yourcertshere;
    #ssl_certificate_key yourkeyhere;


    location /buildByName {
    	rewrite ^(/buildByName/.*) /build/$jobUUIDFromName last;
    }

    location /build {
    	auth_basic &quot;Rundeck Build Server&quot;;
	# guide how to create this is here:
	# https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/
	auth_basic_user_file /etc/nginx/htpasswd;

        mirror /_NULL;                    # Create a copy of the request to capture request body
        client_body_in_single_buffer on;  # Minimize memory copy operations on request body
        client_body_buffer_size      256k; # Largest body to keep in memory (before writing to file)
        client_max_body_size         256k;

        proxy_pass_request_headers off;
	proxy_pass_request_body off;
	proxy_set_body $webhook;

	# best create a fixed token for Rundeck to allow from here, can be done as described in:
	# https://docs.rundeck.com/docs/api/#token-authentication using the framework.properties
	proxy_set_header 'X-Rundeck-Auth-Token' 'yourtoken';

	proxy_set_header 'Accept' 'application/json';
	proxy_set_header 'Content-Type' 'application/json';

        proxy_pass http://localhost:4440/api/31/job/$jobid/run;
    }

    location = /_NULL {
        internal;
        return 204;
    }

    # actually proxy Rundeck
    location / {
        proxy_pass http://localhost:4440;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And finally the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/nginx/webhook.js&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// grab the payload from the incoming POST
function payload_convert(req) {
	// place it in a try/catch in case the JSON can't create the object
	try {
		if ( req.variables.request_body.length &amp;gt; 0 ) {
			var buf = {};
			buf['options'] = {};
			buf['options']['payload'] = req.variables.request_body;

			return JSON.stringify(buf);
		} else {
			req.return(500, &quot;Body cannot be empty&quot;); 
		}
	} catch (e) {
		// log the exception
		req.log(e);
		req.return(500, &quot;Something went horribly wrong&quot;); 
	}
}

// get the job id
function getJobID(req) {
	return req.uri.replace('/build/', '');
}

function getBuildByName(req) {
	// list of jobs and their uuids
	var jobs = {
		'mycooljob': '5fdf3803-d19d-4436-91d3-2d700904dbfb'
	};

	var jobName = req.uri.replace('/buildByName/', '');	
	
	return jobs[jobName];
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;rundeck&quot;&gt;Rundeck&lt;/h2&gt;
&lt;p&gt;Ensure you have a Token set you can always use by nginx, e.g. create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/rundeck/tokens.properties&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Note: the user has to exist!&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;nginx: my-super-duper-token
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/rundeck/framework.properties&lt;/code&gt; we add:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rundeck.tokens.file=/etc/rundeck/tokens.properties
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;rundeck-job&quot;&gt;Rundeck Job&lt;/h2&gt;

&lt;p&gt;Ensure your job accept an option called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;payload&lt;/code&gt; as a text field.&lt;/p&gt;</content><author><name>terwey</name></author><category term="rundeck" /><category term="webhook" /><category term="github" /><category term="nginx" /><category term="njs" /><summary type="html">We wanted to start using Rundeck more to replace an aging Jenkins system. However one thing was really missing from Rundeck, which was the ability to accept an incoming Webhook from Github. This boggled my mind for a bit, why isn’t this simply accepted. If we have a parameter called payload we’re good to go as a POST right. All I needed was something that could convert between the POST that Github sends as a Webhook and what Rundeck wants through their API.</summary></entry><entry><title type="html">Debian Stretch Reprepro ‘The following signatures were invalid’</title><link href="http://leethax.org/2018/03/05/debian-stretch-apt-sha512.html" rel="alternate" type="text/html" title="Debian Stretch Reprepro ‘The following signatures were invalid’" /><published>2018-03-05T19:11:00+00:00</published><updated>2018-03-05T19:11:00+00:00</updated><id>http://leethax.org/2018/03/05/debian-stretch-apt-sha512</id><content type="html" xml:base="http://leethax.org/2018/03/05/debian-stretch-apt-sha512.html">&lt;p&gt;I’ve been using reprepro for a while now to run a Debian APT repo. Which worked fine but on Stretch it has been throwing errors left and right.&lt;/p&gt;

&lt;p&gt;If you add an existing distro upstream you’ll get something like the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;W: GPG error: https://somehost.tld distro InRelease: The following signatures were invalid: HASH
W: The repository 'https://somehost.tld distro InRelease' is not signed.
N: Data from such a repository can't be authenticated and is therefore potentially dangerous to use.
N: See apt-secure(8) manpage for repository creation and user configuration details.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is due to Stretch no longer allowing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHA1&lt;/code&gt; signing because it’s insecure or whatever.&lt;/p&gt;

&lt;p&gt;However, nothing seems to properly show what to do to fix this, so here goes:&lt;/p&gt;

&lt;h1 id=&quot;gnupg-configuration&quot;&gt;GnuPG configuration&lt;/h1&gt;

&lt;p&gt;Important things to do, first off we got to tell GnuPG to no longer prefer SHA1 at all.&lt;/p&gt;

&lt;p&gt;Open up &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.gnupg/gpg.conf&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Prioritize stronger algorithms for new keys.
default-preference-list SHA512 SHA384 SHA256 BZIP2 ZLIB ZIP Uncompressed
# Use a stronger digest than the default SHA1 for certifications.
cert-digest-algo SHA512

# these are critical, without it it'll keep using SHA1 for whatever reason
personal-digest-preferences SHA512
personal-cipher-preferences SHA512

# Optional, but recommended
default-key YOUR-NEW-KEY-HASH
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If your key was 2048 bits at least, you’re now good to go. In my case it was simply a matter of removing the new Stretch distro dir (since I had no packages in it anyway) and having reprepro generate it on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;includedeb&lt;/code&gt;.
After that moment it was working for me.&lt;/p&gt;

&lt;h2 id=&quot;journey-goes-on&quot;&gt;Journey goes on…&lt;/h2&gt;

&lt;p&gt;Secondly, if your original key wasn’t atleast 2048bit yet you’ll have to either create a new key or update the existing one.&lt;/p&gt;

&lt;h3 id=&quot;new-key&quot;&gt;New Key&lt;/h3&gt;

&lt;p&gt;To create a new one, quite easy just do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg --gen-key&lt;/code&gt; and follow the steps. With the new config in place you’ll be good to go.
Afterwards just do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg --list-keys&lt;/code&gt; and take the new sub-key hash and configure reprepro to use it (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/reprepro/dir/conf/distributions&lt;/code&gt;) and mark that for the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SignWith: YOUR-NEW-KEY-HASH
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can also add it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.gnupg/gpg.conf&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;default-key YOUR-NEW-KEY-HASH
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;update-the-key&quot;&gt;Update the Key&lt;/h3&gt;

&lt;p&gt;Now if you want to use an existing key so you don’t have to replace it everywhere (it’ll keep working for pre-Stretch, so this is good) we have to do a bit more steps.&lt;/p&gt;

&lt;p&gt;To remove the SHA1 digest from the key these steps are needed, we’ll need to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg --edit-key KEYHASH&lt;/code&gt;.
Now this command is very arcane and now very clear so here are some notes of what I found.&lt;/p&gt;

&lt;p&gt;When I do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg --edit-key ABCDEFG&lt;/code&gt; I get an interactive prompt:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gpg&amp;gt; showpref
[ultimate] (1). terwey &amp;lt;foo@bar.com&amp;gt;
     Cipher: AES256, AES192, AES, CAST5, 3DES
     Digest: SHA512, SHA384, SHA256, SHA224, SHA1
     Compression: BZIP2, ZLIB, ZIP, Uncompressed
     Features: MDC, Keyserver no-modify
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;But&lt;/em&gt; removing something isn’t so easy, it has to be in the format this system understands&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gpg&amp;gt; pref
[ultimate] (1). terwey &amp;lt;foo@bar.com&amp;gt;
     S9 S8 S7 S3 H10 H9 H8 H11 Z3 Z2 Z1 Z0 [mdc] [no-ks-modify]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ehr, yeah ok, great what does this mean? It’s the same info as before just the internal values, he’s a little table.
Everything prefixed with an S is a Cipher, H is the Digest and Z is the compression.&lt;/p&gt;

&lt;p&gt;This table is based on me playing a bit with the options it can set and what it returned, it’s &lt;em&gt;not&lt;/em&gt; gospel.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;GNUPG Code&lt;/th&gt;
      &lt;th&gt;Meaning&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;S13&lt;/td&gt;
      &lt;td&gt;CAMELLIA256&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;S12&lt;/td&gt;
      &lt;td&gt;CAMELLIA192&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;S11&lt;/td&gt;
      &lt;td&gt;CAMELLIA128&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;S10&lt;/td&gt;
      &lt;td&gt;TWOFISH&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;S9&lt;/td&gt;
      &lt;td&gt;AES256&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;S8&lt;/td&gt;
      &lt;td&gt;AES192&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;S7&lt;/td&gt;
      &lt;td&gt;AES&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;S4&lt;/td&gt;
      &lt;td&gt;BLOWFISH&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;S3&lt;/td&gt;
      &lt;td&gt;CAST5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;H11&lt;/td&gt;
      &lt;td&gt;SHA224&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;H10&lt;/td&gt;
      &lt;td&gt;SHA512&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;H9&lt;/td&gt;
      &lt;td&gt;SHA384&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;H8&lt;/td&gt;
      &lt;td&gt;SHA256&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;H3&lt;/td&gt;
      &lt;td&gt;RIPEMD160&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;H2&lt;/td&gt;
      &lt;td&gt;SHA1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;H1&lt;/td&gt;
      &lt;td&gt;MD5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Z3&lt;/td&gt;
      &lt;td&gt;BZIP2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Z2&lt;/td&gt;
      &lt;td&gt;ZLIB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Z1&lt;/td&gt;
      &lt;td&gt;ZIP&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Z0&lt;/td&gt;
      &lt;td&gt;Uncompressed&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;So it seems what I want is the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gpg&amp;gt; setpref S9 S8 H10 H8 Z3 Z2 Z1 Z0
Set preference list to:
     Cipher: AES256, AES192, 3DES
     Digest: SHA512, SHA256, SHA1
     Compression: BZIP2, ZLIB, ZIP, Uncompressed
     Features: MDC, Keyserver no-modify
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s weird, SHA1 is still there… digging a little deeper in &lt;a href=&quot;https://tools.ietf.org/html/rfc4880#section-13.3.2&quot;&gt;RFC 4880 - OpenPGP Message Format&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Since SHA1 is the MUST-implement hash algorithm, if it is not
explicitly in the list, it is tacitly at the end.  However, it is
good form to place it there explicitly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So yeah, guess for my key it doesn’t really matter. However it could be in your key it’s at the front and then it’s the &lt;em&gt;preferred&lt;/em&gt; digest to use.
In that case, save the key with the new preference and resign the repo.&lt;/p&gt;</content><author><name>terwey</name></author><category term="debian" /><category term="apt" /><category term="reprepro" /><category term="digest" /><category term="key" /><category term="signatures" /><category term="invalid" /><summary type="html">I’ve been using reprepro for a while now to run a Debian APT repo. Which worked fine but on Stretch it has been throwing errors left and right.</summary></entry><entry><title type="html">systemd timers - how to restart a service every X hours</title><link href="http://leethax.org/2017/11/17/systemd-timers.html" rel="alternate" type="text/html" title="systemd timers - how to restart a service every X hours" /><published>2017-11-17T15:09:00+00:00</published><updated>2017-11-17T15:09:00+00:00</updated><id>http://leethax.org/2017/11/17/systemd-timers</id><content type="html" xml:base="http://leethax.org/2017/11/17/systemd-timers.html">&lt;p&gt;Sometimes you have to face nasty service crashes, e.g. due to memory leaks (yes, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logstash&lt;/code&gt;, I’m talking about you).
Using the systemd timers feature is a handy and idiomatic way to restart systemd services (ofc we could use cron, but that’s not what this is about anyway).
However, everyone knows by now that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemd&lt;/code&gt; can be mystical sometimes and its arcanes are somehow complex to understand.&lt;/p&gt;

&lt;p&gt;That being said, I want to restart my logstash service 4 times after it’s been started. Therefore I need two files:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A service unit file that restarts my logstash service&lt;/li&gt;
  &lt;li&gt;A timer file that triggers my restart service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The restart service unit file looks straightforward:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# /etc/systemd/system/logstash-restart.service
[Unit]
Description=restart logstash

[Service]
Type=oneshot
ExecStart=/bin/systemctl restart logstash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, I struggled a bit on how to restart the service every 4 hours after activation.
I did not want to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OnCalendar&lt;/code&gt; option because that would be akin to using cron and wouldn’t leverage the granularity of systemd timer functions.
As it turns out, timers can be scheduled to be activated at a given time interval after they’ve been started with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OnActiveSec&lt;/code&gt; directive but that would only kick in once.
The timer needs to be told to activate again after its initial activation with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OnUnitActiveSec&lt;/code&gt; directive.
Eventually, the time when the timer was activated can be stored on disk and that can be done with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Persistent&lt;/code&gt; option. This option is false by default, which is OK because it doesn’t need to be enabled in my case - I want the timer to activate only after the service has been running for a given number of hours.
This gives us the following timer file:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# /etc/systemd/system/logstash-restart.timer
[Timer]
OnActiveSec=4h
OnUnitActiveSec=4h

[Install]
WantedBy=timer.target
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With both of those files on disk, there remains the need to enable and start the timer (that creates a symbolic link at the appropriate target location so it will be started at next boot, and also starts the countdown from now on):&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# systemctl enable logstash-restart.timer
# systemctl start logstash-restart.timer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Checking if the timer shows up:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# systemctl list-timers --all
NEXT                         LEFT          LAST                         PASSED       UNIT                         ACTIVATES
Fri 2017-11-17 20:32:19 CET  3h 59min left Thu 2017-11-16 19:50:23 CET  20h ago      logstash-restart.timer       logstash-restart.service
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;systemd tracks execution times closely so it’s easy to tell whenever the timer has fired and when it will activate next.&lt;/p&gt;</content><author><name>tanji</name></author><category term="systemd" /><category term="linux" /><category term="sysadmin" /><summary type="html">Sometimes you have to face nasty service crashes, e.g. due to memory leaks (yes, logstash, I’m talking about you). Using the systemd timers feature is a handy and idiomatic way to restart systemd services (ofc we could use cron, but that’s not what this is about anyway). However, everyone knows by now that systemd can be mystical sometimes and its arcanes are somehow complex to understand.</summary></entry><entry><title type="html">True Headless Raspberry Zero W for Spotifyd</title><link href="http://leethax.org/2017/11/08/rasp.html" rel="alternate" type="text/html" title="True Headless Raspberry Zero W for Spotifyd" /><published>2017-11-08T12:00:00+00:00</published><updated>2017-11-08T12:00:00+00:00</updated><id>http://leethax.org/2017/11/08/rasp</id><content type="html" xml:base="http://leethax.org/2017/11/08/rasp.html">&lt;p&gt;I finally decided to buy a Raspberry Pi Zero W, since well it was all the rage and for a specific project I couldn’t use my Unwired One. So this drove me nuts, I actually had to attach a monitor for it to boot.&lt;/p&gt;

&lt;p&gt;The problem is that from Mac/Windows you cannot see the other partitions besides &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot&lt;/code&gt;. However luckily there is a trick that it can copy files from there to the appropriate directories. Turns out other people are lazy too ;)&lt;/p&gt;

&lt;p&gt;Here are the steps I took to make a Raspberry Pi boot without having to attach a monitor and have it auto-join your WiFi on first boot!&lt;/p&gt;

&lt;p&gt;This assumes you flashed the card with Raspbian, whichever version I guess (I used Lite).&lt;/p&gt;

&lt;h2 id=&quot;prepping&quot;&gt;Prepping&lt;/h2&gt;
&lt;p&gt;Mount the device’s card on a machine, I used OSX but on Linux it should be the same.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Open up the mounted SD card, this should be the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot&lt;/code&gt; volume.&lt;/li&gt;
  &lt;li&gt;Create an empty file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt;. 
This will enable SSH on bootup.&lt;/li&gt;
  &lt;li&gt;Then create a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wpa_supplicant.conf&lt;/code&gt; add the following to it and adjust as needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
    ssid=&quot;YOUR_SSID&quot;
    psk=&quot;YOUR_SSID_PASSWORD&quot;
    key_mgmt=WPA-PSK
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On boot this file will be copied to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/wpa_supplicant/wpa_supplicant.conf&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now there should be 2 extra files on your SD card, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wpa_supplicant.conf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After booting these files will vanish from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot&lt;/code&gt; and will be copied to the appropriate locations.&lt;/p&gt;

&lt;p&gt;Now you unmount the SD-Card and stick it in your Raspberry Pi Zero W and have it boot and it will automatically join the network. Check your Router for the IP address it joined with.
You’ll now be able to login using user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pi&lt;/code&gt; and password &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raspberry&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;spotifyd&quot;&gt;Spotifyd&lt;/h1&gt;

&lt;p&gt;So I wanted to have a Spotify Connect device, since I got a nice stereo and it doesn’t natively have networking capabilities.&lt;/p&gt;

&lt;p&gt;Turns out there’s an awesome Github project: &lt;a href=&quot;https://github.com/Spotifyd/spotifyd&quot;&gt;Spotifyd/spotifyd&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;
&lt;p&gt;Time to setup the Spotifyd, you can grab the latest on their releases page.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget https://github.com/Spotifyd/spotifyd/releases/download/untagged-9611d797f6d3344e6a8e/spotifyd-2017-07-22-armv7.zip
unzip spotifyd-2017-07-22-armv7.zip
chmod +x spotifyd
mv spotifyd /usr/bin/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then we create a file for Spotifyd to work from, I took the example from their README, excellent stuff. This was the bare minimum I needed since I already set the default device previous. I have to software control over the volume, but it does work from Spotify’s UI.&lt;/p&gt;

&lt;p&gt;The following should go in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.config/spotifyd/spotifyd.conf&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[global]
backend = alsa
device_name = ZeroW
bitrate = 320
device = plughw:CARD=Audio # add this (from `aplay -L`) when you skipped the systemwide audio device
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you skipped the systemwide audio setup, you’ll have to check with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aplay -L&lt;/code&gt; which &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;device&lt;/code&gt; you need. I recommend you to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plughw&lt;/code&gt; one to prevent resampling issues.&lt;/p&gt;

&lt;p&gt;For more advanced things, like caching of files, go checkout their Github project, it’s well documented.&lt;/p&gt;

&lt;h2 id=&quot;systemd&quot;&gt;systemd&lt;/h2&gt;

&lt;p&gt;Spotifyd also has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemd service&lt;/code&gt; file available for automatic starting of the daemon on boot.&lt;/p&gt;

&lt;p&gt;I want the Spotifyd daemon to run on system start without logging in.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget https://raw.githubusercontent.com/Spotifyd/spotifyd/master/contrib/spotifyd.service
sudo mv spotifyd.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable spotifyd.service
sudo mv .config/spotifyd/spotifyd.conf /etc/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now make the following adjustment to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/systemd/system/spotifyd.service&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;## existing
ExecStart=/usr/bin/spotifyd --no-daemon
## change to
ExecStart=/usr/bin/spotifyd --no-daemon --config /etc/spotifyd.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it for having it auto start :)&lt;/p&gt;

&lt;h3 id=&quot;nad-dac&quot;&gt;NAD DAC&lt;/h3&gt;

&lt;p&gt;So the first test with Spotifyd was done using a FiiO E10 DAC, now I thought, great time to wrap this up and attach my NAD DAC 1. Yeah so here the problems began. My initial testing I used the FiiO using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type hw&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type plughw&lt;/code&gt;. This was fine, since the FiiO could do both 44.1kHz and 48kHz. The NAD DAC 1 only takes 48000Hz, and Spotify feeds it 44100Hz which ALSA then has to do something with.&lt;/p&gt;

&lt;p&gt;This can be fixed though, turns out for this card I did have to define an actual device in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spotifyd.conf&lt;/code&gt;, so I went back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aplay -L|grep -A1 plughw&lt;/code&gt; and got this as output:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;plughw:CARD=Headset,DEV=0
    USB Headset, USB Audio
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For resampling to work, we need to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plughw:CARD=Headset&lt;/code&gt; one and not the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hw&lt;/code&gt; one. I couldn’t get it to work purely with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;asound.conf&lt;/code&gt; so I gave up and just added the following to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.config/spotifyd/spotify.conf&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;device = plughw:CARD=Headset
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;optional-systemwide-audio&quot;&gt;Optional Systemwide audio&lt;/h3&gt;

&lt;p&gt;On the Pi we have to do a few things, first find out your Audio device name (I use an external DAC, saves a lot of hassle), you can do so with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aplay -l&lt;/code&gt; however this command will output the valid names for the config file.
I couldn’t get this working with my NAD DAC. It did work with my FiiO DAC.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ aplay -l | awk -F \: '/,/{print $2}' | awk '{print $1}' | uniq
ALSA
Audio # this is the one I need
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The following can then be placed in the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/asound.conf&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pcm.!default {
        type hw
        card Audio
}

ctl.!default {
        type hw
        card Audio
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>terwey</name></author><category term="raspberrypizerow" /><category term="raspbian" /><summary type="html">I finally decided to buy a Raspberry Pi Zero W, since well it was all the rage and for a specific project I couldn’t use my Unwired One. So this drove me nuts, I actually had to attach a monitor for it to boot.</summary></entry><entry><title type="html">Prometheus Slack Alerts with links to Grafana</title><link href="http://leethax.org/2017/11/06/prometheus-alerts-slack-grafana.html" rel="alternate" type="text/html" title="Prometheus Slack Alerts with links to Grafana" /><published>2017-11-06T18:45:00+00:00</published><updated>2017-11-06T18:45:00+00:00</updated><id>http://leethax.org/2017/11/06/prometheus-alerts-slack-grafana</id><content type="html" xml:base="http://leethax.org/2017/11/06/prometheus-alerts-slack-grafana.html">&lt;p&gt;&lt;a href=&quot;https://prometheus.io&quot;&gt;Prometheus&lt;/a&gt; is an excellent timeseries-based monitoring platform. As well, it’s easily pluggable with the equally excellent dashboard software, &lt;a href=&quot;https://grafana.com&quot;&gt;Grafana&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One of my colleagues thought it would be really cool if we got links to the Grafana dashboard for the given server when an alert is received in Slack. That looked possible, because the Grafana dashboard links use the node name and port as URL parameters, see below:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;http://grafana:3000/dashboard/db/node-exporter-full?refresh=1m&amp;amp;orgId=1&amp;amp;var-node=web-01&amp;amp;var-port=9100
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var-node&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var-port&lt;/code&gt; parameters just need to be filled with this info. However, this is received from Prometheus with a single label, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;instance=web-01:9100&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can use the power of Prometheus templating functions to generate a URL using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reReplaceAll&lt;/code&gt; regular expression function.&lt;/p&gt;

&lt;p&gt;For example, the following template will capture the left part of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;instance&lt;/code&gt; label (the host name), and output it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{{ reReplaceAll &quot;(.*):(.*)&quot; &quot;${1}&quot; .Labels.instance }}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here goes our final template to generate a slack notification including the URL, to be added to the alertmanager configuration:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;receivers:
- name: ops-slack
  slack_configs:
  - username: prometheus
    send_resolved: true
    channel: alerts
    title: '[{{ .Status | toUpper }}{{ if eq .Status &quot;firing&quot; }}:{{ .Alerts.Firing | len }}{{ end }}] Monitoring Event Notification'
    text: &amp;gt;-
        {{ range .Alerts }}
          *Alert:* {{ .Annotations.summary }} - `{{ .Labels.severity }}`
          *Description:* {{ .Annotations.description }}
          *Graph:* &amp;lt;http://grafana.com:3000/dashboard/db/node-exporter-full?refresh=1m&amp;amp;orgId=1&amp;amp;var-node={{ reReplaceAll &quot;(.*):(.*)&quot; &quot;${1}&quot; .Labels.instance }}&amp;amp;var-port={{ reReplaceAll &quot;(.*):(.*)&quot; &quot;${2}&quot; .Labels.instance }} |:chart_with_upwards_trend:&amp;gt;
          {{ end }}
        {{ end }}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>tanji</name></author><category term="prometheus" /><category term="slack" /><category term="grafana" /><summary type="html">Prometheus is an excellent timeseries-based monitoring platform. As well, it’s easily pluggable with the equally excellent dashboard software, Grafana.</summary></entry><entry><title type="html">Unwired One / Blackswift stuff</title><link href="http://leethax.org/2017/11/05/unwiredone.html" rel="alternate" type="text/html" title="Unwired One / Blackswift stuff" /><published>2017-11-05T12:00:00+00:00</published><updated>2017-11-05T12:00:00+00:00</updated><id>http://leethax.org/2017/11/05/unwiredone</id><content type="html" xml:base="http://leethax.org/2017/11/05/unwiredone.html">&lt;p&gt;Quite a while ago I backed the Kickstarter campaign for the &lt;a href=&quot;https://www.kickstarter.com/projects/1133560316/black-swift-tiny-wireless-computer&quot;&gt;Black Swift&lt;/a&gt; and did receive it. However I didn’t really do anything with it, and by the time I received it it was already renamed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unwired One&lt;/code&gt;. I played with it once in January 2017 and back then had huge problems getting the WiFi to work. Turns out factory default the internal IP it uses for LAN is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.1.1&lt;/code&gt; which is the same as most routers your home network is configured with.
Fast forward to November 2017 when I had an idea for a project (which I’m describing here) and I again totally forgot about this bug. So I’ll be documenting it here for prosperity as the original Unwired One forums are offline :(&lt;/p&gt;

&lt;h1 id=&quot;preparation&quot;&gt;Preparation&lt;/h1&gt;

&lt;h2 id=&quot;fixing-the-wifi&quot;&gt;Fixing the WiFi&lt;/h2&gt;
&lt;p&gt;First join the built-in WiFi &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unwired One&lt;/code&gt; this is an unprotected access point. Then grab a Terminal and SSH in, username &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; password &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We have to change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lan&lt;/code&gt; interface to not be on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.1.1&lt;/code&gt; but something else.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ssh root@192.168.0.254
$ vi /etc/config/network # or nano, whichever you prefer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Make the following changes:&lt;/p&gt;

&lt;p&gt;Original:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;config interface 'lan'
        option ifname 'eth1'
        option force_link '1'
        option type 'bridge'
        option proto 'static'
        option ipaddr '192.168.1.1' # change this line!
        option netmask '255.255.255.0'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;New:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;config interface 'lan'
        option ifname 'eth1'
        option force_link '1'
        option type 'bridge'
        option proto 'static'
        option ipaddr '192.168.5.1'
        option netmask '255.255.255.0'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Save the file, and do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/init.d/network restart&lt;/code&gt; or simply a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reboot&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;upgrading-the-firmware&quot;&gt;Upgrading the Firmware&lt;/h2&gt;
&lt;p&gt;When the device comes back online, let’s do an upgrade of the firmware, as mine was still running the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BARRIER BREAKER (Barrier Breaker, r47068) 2015-10-15 13:09:30&lt;/code&gt; release. The latest one you can find here: &lt;a href=&quot;https://files.unwireddevices.com/files/openwrt/ccalmer/latest/openwrt-ar71xx-generic-unwone-squashfs-sysupgrade.bin&quot;&gt;openwrt-ar71xx-generic-unwone-squashfs-sysupgrade.bin&lt;/a&gt;.
The manual says you can do a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wget&lt;/code&gt; directly on the device, this however didn’t work for me so I just copied to the file via SCP to the device, be sure to do this to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt; dir as that’s the memory mounted device &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmpfs&lt;/code&gt; drive.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ scp openwrt-ar71xx-generic-unwone-squashfs-sysupgrade.bin 192.168.0.254:/tmp
$ ssh 192.168.0.254
$ sysupgrade -v /tmp/openwrt-ar71xx-generic-unwone-squashfs-sysupgrade.bin # add the -n flag to reset to factory defaults
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After this it will reboot and come back online with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CHAOS CALMER (Chaos Calmer, r49404) 2016-08-16 00:18:25&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;joining-the-wifi&quot;&gt;Joining the WiFi&lt;/h2&gt;
&lt;p&gt;Now we can simply follow the &lt;a href=&quot;https://www.unwireddevices.com/wiki/index.php/Configuring_Wi-Fi_Network&quot;&gt;Instructions from the Manual&lt;/a&gt; but to rehash I’ll give the gist here.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Go to &lt;a href=&quot;http://192.168.0.254&quot;&gt;192.168.0.254&lt;/a&gt; with a browser&lt;/li&gt;
  &lt;li&gt;In the menu bar hover &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Network&lt;/code&gt; and then click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Wifi&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Hit the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Scan&lt;/code&gt; button&lt;/li&gt;
  &lt;li&gt;Find your network, click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Join Network&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Fillout your password, and assign the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lan&lt;/code&gt; firewall zone.&lt;/li&gt;
  &lt;li&gt;Hit save, wait for the next page to load and click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Save and Apply&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Wait for it to come back up joining your WiFi, at this point you’ll be kicked off the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unwired One&lt;/code&gt; network.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now login to your router to see which new DHCP lease was given off to the device, or use a Bonjour Browser to find it.&lt;/p&gt;

&lt;h2 id=&quot;ssh-refused&quot;&gt;SSH Refused&lt;/h2&gt;

&lt;p&gt;If you’re locked out of your Unwired One by SSH but can access the WebUI, the UI solution might work too. It’s written below.&lt;/p&gt;

&lt;p&gt;I had the case I got locked out of the SSH but not the WebUI, on inspecting the logs I saw the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;authpriv.warn dropbear[3222]: Failed loading /etc/dropbear/dropbear_ecdsa_host_key
authpriv.warn dropbear[3222]: Failed listening on '22': Error listening: Address already in use
authpriv.info dropbear[3222]: Early exit: No listening ports available.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which led me to believe the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dropbear&lt;/code&gt; configuration was broken, so I went over to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System -&amp;gt; Administration&lt;/code&gt; interface and added a second SSH port (in my case I used 222) on an unspecified network.&lt;/p&gt;

&lt;p&gt;On further inspection I noticed that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sshd&lt;/code&gt; was running:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root@UnwiredOne:~# netstat -anp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:2222            0.0.0.0:*               LISTEN      3934/dropbear
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      2532/sshd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After doing a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/init.d/sshd stop&lt;/code&gt; and an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/init.d/dropbear restart&lt;/code&gt; I was suddenly able to login again to my Unwired One.&lt;/p&gt;

&lt;p&gt;My current &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/config/dropbear&lt;/code&gt; now looks like this.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;config dropbear
        option PasswordAuth 'on'
        option Port '22'

config dropbear
        option PasswordAuth 'on'
        option Port '2222'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;disabling-sshd-from-the-ui&quot;&gt;Disabling SSHD from the UI&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;System -&amp;gt; Startup&lt;/li&gt;
  &lt;li&gt;Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enable&lt;/code&gt; for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sshd&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stop&lt;/code&gt; for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sshd&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Restart&lt;/code&gt; for Dropbear&lt;/li&gt;
&lt;/ul&gt;</content><author><name>terwey</name></author><category term="unwiredone" /><category term="blackswift" /><summary type="html">Quite a while ago I backed the Kickstarter campaign for the Black Swift and did receive it. However I didn’t really do anything with it, and by the time I received it it was already renamed to Unwired One. I played with it once in January 2017 and back then had huge problems getting the WiFi to work. Turns out factory default the internal IP it uses for LAN is 192.168.1.1 which is the same as most routers your home network is configured with. Fast forward to November 2017 when I had an idea for a project (which I’m describing here) and I again totally forgot about this bug. So I’ll be documenting it here for prosperity as the original Unwired One forums are offline :(</summary></entry><entry><title type="html">Headless Teamviewer with custom resolution</title><link href="http://leethax.org/2017/03/08/headless-teamviewer-custom-resolution.html" rel="alternate" type="text/html" title="Headless Teamviewer with custom resolution" /><published>2017-03-08T22:45:00+00:00</published><updated>2017-03-08T22:45:00+00:00</updated><id>http://leethax.org/2017/03/08/headless-teamviewer-custom-resolution</id><content type="html" xml:base="http://leethax.org/2017/03/08/headless-teamviewer-custom-resolution.html">&lt;p&gt;I wrote this to use Teamviewer on a remote machine without a graphics-card but do want to use high resolutions. I wanted to use full-screen on my 21:9 shinyness which does 3440x1440. However I couldn’t get past 1324x768 with normal configs.
Hard pressed to get it working I decided to sacrifice an evening to it. It now works, and it’s gorgeous :)&lt;/p&gt;

&lt;p&gt;On Debian Jessie this file was found at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/share/X11/xorg.conf.d/xorg.conf&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Other packages needed were: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xserver-xorg-video-dummy&lt;/code&gt; and Teamviewer.&lt;/p&gt;

&lt;p&gt;Sample of Teamviewer showing the new available resolutions:
&lt;img src=&quot;http://leethax.org/assets/teamviewer.png&quot; alt=&quot;Teamviewer preview&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Xorg.conf config for dummy video driver
# For usage with for example TeamViewer on a machine without a monitor attached
# and you wanted more then just 1024x768 ;)
#
# Use at own risk, loosly based on info scattered around but these links really helped
# http://arachnoid.com/modelines/ for the modelines (lot of trial and error to figure out which worked over Teamviewer and Xorg)
# https://www.xpra.org/xorg.conf sample config from xpra who seems to use the dummy driver a lot (thanks guys!)

Section &quot;Device&quot;
	Identifier &quot;dummy_videocard&quot;
	Option &quot;NoDDC&quot; &quot;true&quot;
	Option &quot;IgnoreEDID&quot; &quot;true&quot;

	# Debian: apt-get install xserver-xorg-video-dummy
	Driver &quot;dummy&quot;

	# You could lower this to suit your needs, however you will need
	# a quite high amount to run such crazy resolutions
	VideoRam 524288
EndSection

Section &quot;Monitor&quot;
	Identifier &quot;dummy_monitor&quot;

	# 16:9 4k
	Modeline &quot;3840x2160_20.00&quot; 218.15 3840 4016 4416 4992 2160 2161 2164 2185

	# 21:9 &quot;4k&quot;
	Modeline &quot;3440x1440_20.00&quot; 124.95 3440 3520 3864 4288 1440 1441 1444 1457

	# Usual 27&quot; suspect before 4k era
	Modeline &quot;2560x1440&quot; 42.12 2560 2592 2752 2784 1440 1475 1478 1513

	# Oddball 1920 resolution
	Modeline &quot;1920x1440&quot; 69.47 1920 1960 2152 2384 1440 1441 1444 1457

	# 16:10 &quot;Full-HD&quot;
	Modeline &quot;1920x1200&quot; 26.28 1920 1952 2048 2080 1200 1229 1231 1261

	# 16:9 Full HD
	Modeline &quot;1920x1080&quot; 23.53 1920 1952 2040 2072 1080 1106 1108 1135

	# These are just crazy high to ensure stuff works
	HorizSync   5.0 - 1000.0
	VertRefresh 5.0 - 1000.0
EndSection

Section &quot;Screen&quot;
	Identifier &quot;dummy_screen&quot;
	Device &quot;dummy_videocard&quot;
	Monitor &quot;dummy_monitor&quot;
	DefaultDepth 24
	SubSection &quot;Display&quot;
		Depth 24
		Modes &quot;3840x2160_20.00&quot; &quot;3440x1440_20.00&quot; &quot;2650x1440&quot; &quot;1920x1440&quot; &quot;1920x1200&quot; &quot;1920x1080&quot;

		# Not sure why, but 3440x1440 won't work when the Virtual is set to &quot;3840 2160&quot;
		# However it will complain in the Xorg.log when you didn't comment out the 3840x2160 resolution at the top
		#
		# Uncomment this for 21:9 4k
		Virtual 3440 1440

		# Uncomment this for 16:9 4k
		#Virtual 3840 2160
	EndSubSection
EndSection
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>terwey</name></author><category term="teamviewer" /><category term="xorg" /><summary type="html">I wrote this to use Teamviewer on a remote machine without a graphics-card but do want to use high resolutions. I wanted to use full-screen on my 21:9 shinyness which does 3440x1440. However I couldn’t get past 1324x768 with normal configs. Hard pressed to get it working I decided to sacrifice an evening to it. It now works, and it’s gorgeous :)</summary></entry></feed>