IDN and Private RR in Go DNS

Thanks to the excellent work from Alex Sergeyev, Go DNS has gotten some new features. I want to highlight two: IDN (https://www.ietf.org/rfc/rfc3492.txt) and Private RR support (http://tools.ietf.org/html/rfc6895).

IDN

This adds support for converting from and to Punycode. There is no explicit support, you will need to call idn.ToPunycode and idn.FromPunyCode yourself if you are dealing with IDNs.

The examples give in the code:

name := "インターネット.テスト"
fmt.Printf("%s -> %s", name, idn.ToPunycode(name))

Which outputs:

インターネット.テスト -> xn--eckucmux0ukc.xn--zckzah

Private RR

Another thing that was added is the ability to add private RR types: that is new RR type with a code in the 65,280 - 65,534 range. This makes it possible to specify your own (vanity) types, which then work just as the normal ones in Go DNS.

Say for instance I want to add a new RR called "MIEK" (with type code 65281), this RR has two rdata elements: a domain name and it ends with a hexadecimal string. Basically it looks like:

www.example.com.    IN MIEK     example.com. AABB1234

Note that you can also use TYPExxxx and RFC3597 (http://www.ietf.org/rfc/rfc3597.txt) for this, but then the above RR would look something like this:

www.example.com.    IN TYPE65281 \# 8 AABBCCDDAABB1234

Which works, but of course looks bad in the vanity department.

To do this you will need (see example_test.go for example code in the library) create a new record type, implement the PrivateRdata interface and register the new type with the library.

const typeMiek = 65281
type MIEK struct {
    Target string
    Extra  string
}

Implement PrivateRdata:

  1. String - returns the string presentation of the rdata;
  2. Len - returns the length of the rdata;
  3. Pack and Unpack to convert to and from wire format;
  4. Parse to parse a text presentation of the rdata;
  5. Copy to allow the private RR to be copied inside Go DNS (sometimes needed for DNSSEC).

So lets start with the implementation beginning with String() and Len()

func (m *MIEK) String() string { return m.Target + " " + m.Extra }
func (m *MIEK) Len() int  { return len(m.Target) + len(m.Extra)/2 }

And the functions to convert from and to wire format:

func (m *MIEK) Pack(buf []byte) (int, error) {
        off, err := dns.PackDomainName(m.Target, buf, 0, nil, false)
        if err != nil {
                return off, err
        }
        h, err := hex.DecodeString(m.Extra)
        if err != nil {
                return off, err
        }
        if off+hex.DecodedLen(len(m.Extra)) > len(buf) {
                return len(buf), errors.New("overflow packing hex")
        }
        copy(buf[off:off+hex.DecodedLen(len(m.Extra))], h)
        off += hex.DecodedLen(len(m.Extra))
        return off, nil
}

func (m *MIEK) Unpack(buf []byte) (int, error) {
        s, off, err := dns.UnpackDomainName(buf, 0)
        if err != nil {
                return len(buf), err
        }
        m.Target = s
        s = hex.EncodeToString(buf[off:])
        m.Extra = s
        return len(buf), nil
}

And our Parse function that parses the string slice sx into MIEK's rdata.

func (m *MIEK) Parse(sx []string) error {
        if len(sx) < 2 {
                return errors.New("need at least 2 pieces of rdata")
        }
        m.Target = sx[0]
        if  _, ok := dns.IsDomainName(m.Target); !ok {
                return errors.New("bad MIEK Target")
        }
        // Hex data can contain spaces.
        for _, s := range sx[1:] {
                m.Extra += s
        }
        return nil
}

And Copy which is needed to copy the rdata:

func (m *MIEK) Copy(dest dns.PrivateRdata) error {
        m1, ok := dest.(*MIEK)
        if !ok {
                return dns.ErrRdata
        }
        m1.Target = m.Target
        m1.Extra = m.Extra
        return nil
}

And finally we can register the new type:

dns.PrivateHandle("MIEK", typeMiek, func() dns.PrivateRdata { return new(MIEK) })
defer dns.PrivateHandleRemove(typeMiek) // when removing again

We can now use all this code to do the following:

m1, _ := dns.NewRR("example.com. IN MIEK target.example.com. DEADBEEF")
log.Printf("%s\n", m1.String())

That prints:

 example.com.   3600    IN  MIEK    target.example.com. DEADBEEF

Mission accomplished :) This is all pretty new code, but I'm pretty happy how this is shaping up, but there might be some changes here in the future.

If you need to export this new type, your best bet is to convert it to an unknown RR:

u := new(dns.RFC3597)                                                                                                                                                                                                  
u.ToRFC3597(m1)                                                                                                                                                                                                        
log.Printf("%s\n", u.String())

example.com.    3600    IN  MIEK    \# 24 06746172676574076578616d706c6503636f6d00deadbeef
Tagged ,

Go DNS package

Go DNS is a package that implements a DNS interface in Go. This library takes a new, innovative and enterprise ready approach sends and receives queries to and from the DNS. It is licensed under the same license as the official Go code, as this is a fork of that code.

The aim is to be powerful, simple and fast.

Supported:

  • All RR types;
  • Synchronous and asynchronous queries and replies;
  • DNSSEC: validation, signing, key generation, reading .private key files
  • (Fast) sending/receiving/printing packets, RRs;
  • Full control over what is being send;
  • Zone transfers, EDNS0, TSIG, NSID;
  • Server side programming (a full blown nameserver).
  • (Fast) reading zones/RRs from files/strings.

Code

The git repository is hosted on github.

Examples using this library can be found in exdns repository over at github.

Tutorials and more info.

Printing MX records

A small peek in to how to print MX records with Go DNS.

We want to create a little program that prints out the MX records of domains, like so:

% mx miek.nl
miek.nl.        86400   IN      MX      10 elektron.atoom.net.

Or

% mx microsoft.com 
microsoft.com.  3600    IN      MX      10 mail.messaging.microsoft.com.

First the normal header of a Go program, with a bunch of imports. We need the dns package:

package main

import (
    "github.com/miekg/dns"
    "os"
    "net"
    "fmt"
    "log"
)

Next we need to get the local nameserver to use:

config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")

Then we create a dns.Client to perform the queries for us. In Go:

c := new(dns.Client)

We skip some error handling and assume a zone name is given. So we prepare our question. For that to work, we need:

  1. a new packet (dns.Msg);
  2. setting some header bits;
  3. define a question section;
  4. fill out the question section: os.Args[1] contains the zone name.

Which translates into:

m := new(dns.Msg)
m.SetQuestion(dns.Fqdn(os.Args[1]), dns.TypeMX)
m.RecursionDesired = true

Then we need to finally 'ask' the question. We do this by calling the Exchange() function. The unused return value is the rtt (round trip time).

r, _, err := c.Exchange(m, net.JoinHostPort(config.Servers[0], config.Port))

Check if we got something sane. The following code snippet prints the answer section of the received packet:

Bail out on an error:

if r == nil {
    log.Fatalf("*** error: %s\n", err.Error())
}

if r.Rcode != dns.RcodeSuccess {
        log.Fatalf(" *** invalid answer name %s after MX query for %s\n", os.Args[1], os.Args[1])
}

// Stuff must be in the answer section
for _, a := range r.Answer {
        fmt.Printf("%v\n", a)
}

And we are done.

Full Source

package main

import (
    "github.com/miekg/dns"
    "net"
    "os"
    "log"
    "fmt"
)

func main() {
    config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
    c := new(dns.Client)

    m := new(dns.Msg)
    m.SetQuestion(dns.Fqdn(os.Args[1]), dns.TypeMX)
    m.RecursionDesired = true

    r, _, err := c.Exchange(m, net.JoinHostPort(config.Servers[0], config.Port))
    if r == nil {
        log.Fatalf("*** error: %s\n", err.Error())
    }

    if r.Rcode != dns.RcodeSuccess {
            log.Fatalf(" *** invalid answer name %s after MX query for %s\n", os.Args[1], os.Args[1])
    }
    // Stuff must be in the answer section
    for _, a := range r.Answer {
            fmt.Printf("%v\n", a)
    }
}
Tagged ,

SkyDNS running live

SkyDNS is able to do DNSSEC. It generates signatures and NSEC3 records on the fly. For authenticated denial of existence SkyDNS uses NSEC3 white lies, of course implementing (and testing!) this isn't completely trivial.

To aid in debugging I've setup a live version of SkyDNS on voordeur.atoom.net, under the name the zone http://dnssex.nl:

% dig +mul +noall +answer @voordeur.atoom.net soa skydns.dnssex.nl
skydns.dnssex.nl.    3600 IN SOA ns1.dns.skydns.dnssex.nl. hostmaster.skydns.local. (
                            1403942400 ; serial
                            28800      ; refresh (8 hours)
                            7200       ; retry (2 hours)
                            604800     ; expire (1 week)
                            60         ; minimum (1 minute)
                            )

To help getting DNSSEC support 100% working this zone has been delegated and has an DS record in the parent zone. With unbound-host you can see the validation status of this zone:

% unbound-host -C /etc/unbound/unbound.conf -vt SOA skydns.dnssex.nl 
skydns.dnssex.nl has SOA record ns1.dns.skydns.dnssex.nl. hostmaster.skydns.local. 
    1403942400 28800 7200 604800 60 (secure)

Where (secure) indicates DNSSEC is in order.

Bugs

However getting NXDOMAIN and NODATA response it gets a bit more flaky, but some stuff is working:

% unbound-host -C /etc/unbound/unbound.conf -vt TXT dns.skydns.dnssex.nl
dns.skydns.dnssex.nl has no TXT record (secure)

And some is not:

% unbound-host -C /etc/unbound/unbound.conf -vt SRV server2.miek.skydns.dnssex.nl
Host server2.miek.skydns.dnssex.nl not found: 3(NXDOMAIN). (BOGUS (security failure))
validation failure <server2.miek.skydns.dnssex.nl. SRV IN>: 
    nameerror proof failed from 89.98.74.120

sadface I believe this is due to defaulting to skydns.dnssex.nl as the closest encloser and *.skydns.dnssex.nl as the source of synthesis, but I haven't had the time to dig deeper into this.

Testing

In the near future I hope to update the current test, to include NSEC3 white lies tests.

Tagged , , , , , ,

SkyDNS version 2

SkyDNS version 1 was announced some time ago, since then it has seen some developments, which resulted in SkyDNS version 2. This new version uses Etcd as its backend. This blog post will walk you through the installation and shows how to use it.

What?!

SkyDNS(2) is a service discovery tool that utilizes the DNS to find hosts in a distributed environment. But using DNS means "legacy" clients can be used. Want to know if you MariaDB cluster is still up? ping mariadb.skydns.local can be used for that. By default SkyDNS will use skydns.local. as the domain to anchor all names.

Installation

If not already installed, install Go for your system, either via the package manager or from source. After that you will need Etcd and SkyDNS:

  • go get github.com/coreos/etcd
  • go get github.com/skynetservices/skydns

After the installation, start Etcd: ./etcd. This will run a lonely, non clusterized Etcd on port 4001 on your local machine. SkyDNS has the ability to use configuration stored in Etcd, but for now we use the command line flags to start SkyDNS:

% ./skydns -addr=127.0.0.1:1054 -machines=http://127.0.0.1:4001 \
    -nameservers=8.8.4.4:53
[skydns] Jun  8 08:30:19.761 INFO      | ready for queries

Let's see if it works, by using dig:

% dig @127.0.0.1 -p 1054 +noall +answer +add SOA skydns.local
skydns.local. 3600 IN SOA ns1.dns.skydns.local. hostmaster.skydns.local. (
                1402210800 ; serial
                28800      ; refresh (8 hours)
                7200       ; retry (2 hours)
                604800     ; expire (1 week)
                60         ; minimum (1 minute)
                )

Somebody is answering! Note in the other examples, I will use the same command line for, but remove all the flags and options. If a query aimed at SkyDNS does not fall under skydns.local. it will forward it to 8.8.4.4 and returns the answer from that:

% dig a miek.nl
miek.nl.        19827 IN A 176.58.119.54

With this you can configure SkyDNS as your nameserver in /etc/resolv.conf.

Services

The original SkyDNS used a fix naming scheme, environment.service.version.region.skydns.local., SkyDNS2 does away with this, but still it makes sense to defines some scheme to be used in your environment. In this blog post I will use a very simple scheme that only uses a region, like "east", "west", etc.

Let register a service in Etcd, we want the register the name 'web01.east.skydns.local', which listens on port 80 and has an IP4 address of 10.0.1.3. All names used by SkyDNS in Etcd are stored under /skydns/ and we need to reverse the domain name. So to register the name we need to use the key: /v2/keys/skydns/local/skydns/east/web01, the payload of it must be JSON like so:

% curl -XPUT http://127.0.0.1:4001/v2/keys/skydns/local/skydns/east/web01 \
    -d value='{"Port":80,"Host": "10.0.1.3"}'

And retrieving it via DNS:

% dig A web01.east.skydns.local
web01.east.skydns.local. 3600 IN A 10.0.1.3

Now we also add another webservice in the east region, web02.east.skydns.local, with IP4 address of 10.0.1.4.

Now suppose you want to have a list of all webservers in the east region? Simple just query for east.skydns.local:

% dig A east.skydns.local
east.skydns.local.  3600 IN A 10.0.1.4
east.skydns.local.  3600 IN A 10.0.1.3

Of course IP6 is also supported. Using A and AAAA records allows for "legacy" support, however the port number must be know by the client connection, because that information is not in the returned records. To fix this you can also query for SRV records.

SRV Records

SRV records return much more information than A/AAAA records, it includes a port number, a priority a weight and a name (not an address record). As the service information for web01 only includes an address, SkyDNS will synthesise the SRV record and includes the actual IP address in the additional section:

% dig SRV web01.east.skydns.local
web01.east.skydns.local. 3600 IN SRV 10 100 80 web01.east.skydns.local.
web01.east.skydns.local. 3600 IN A 10.0.1.3

The numbers "10", "100" and "80" in the SRV records are respectively:

  • 10: priority.
  • 20: weight (when multiple SRV records have the same priority, look at the weight). In SkyDNS weight is a percentage.
  • 80: the port number for the service, if the port is not given in the service, it defaults to 0.

Of course this all works when you query for east.skydns.local as well.

Wildcards

The DNS standards supports wildcards, but SkyDNS extends this usage to allow wildcards within a domainname. To show how this we add another service, this time web01.west.skydns.local. Suppose we want to target all web01 servers? With plain DNS you will need to do two queries (and know about west and east!), with SkyDNS only one is needed:

% dig web01.*.skydns.local
web01.*.skydns.local.   3600    IN  SRV 10 50 80 web01.east.skydns.local.
web01.*.skydns.local.   3600    IN  SRV 10 50 80 web01.west.skydns.local.
web01.east.skydns.local. 3600   IN  A   10.0.1.3
web01.west.skydns.local. 3600   IN  A   10.0.2.1

DNSSEC

Signed responses are also supported, although authenticated denial of existence based on NSEC3 is a work in progress. A quick primer on how to enable it, as there are a few steps.

  1. Generate a DNSSEC keypair for SkyDNS:

    % dnssec-keygen skydns.local
    Generating key pair........................................++++++ .....++++++ 
    Kskydns.local.+005+04821
    
  2. Use the basename of the generated key pair as an argument to SkyDNS:

    % ./skydns -addr=127.0.0.1:1054 -machines=http://127.0.0.1:4001 \
        -nameservers=8.8.4.4:53 -dnssec Kskydns.local.+005+04821
    [skydns] Jun  8 12:28:37.981 INFO      | ready for queries, signing with Kskydns.local.+005+04821
    

When you know query with the DO bit on (+dnssec in dig) you will get signed responses:

% dig +dnssec web01.*.skydns.local
web01.*.skydns.local.   3600 IN SRV 10 50 80 web01.east.skydns.local.
web01.*.skydns.local.   3600 IN SRV 10 50 80 web01.west.skydns.local.
web01.*.skydns.local.   3600 IN RRSIG SRV 5 4 3600 (
                     20140615113057 20140608083057 4821 skydns.local.
                     DWNzcR9DqNHrITZPN/sfGUH42Ur+GQXSQu95O4eouY0a
                     8uupN8pa75IA/inJ6tnN7cTW5XGbe6AKLB90GrxEIiHt
                     dxogqzCNaCt/Z4bj8BY8ZMLC5IXX2+L6hnLYlCv6Q/jJ
                     4ixafFhbJSD+Rc4eK764Rberhik/zUtuXDe8kXM= )
web01.east.skydns.local. 3600 IN A 10.0.1.3
web01.west.skydns.local. 3600 IN A 10.0.2.1
web01.east.skydns.local. 3600 IN RRSIG A 5 4 3600 (
                     20140615113057 20140608083057 4821 skydns.local.
                     X9uu3yw2aPUJaJu0q0I0cEyJHAcvpqd1bDhAFEM0uHkr
                     yWQzm+i0VK6HEawK+hgtenfkJb1DtqIUdsEQ7+Kz1elV
                     EC+w/W7p+bXL29BTuJOu722H8tXo21I9lr+SoIoIDsKG
                     BfPkVwACwBAWaPJWrxy90v43NXdSunl55eUVoP4= )
web01.west.skydns.local. 3600 IN RRSIG A 5 4 3600 (
                     20140615113057 20140608083057 4821 skydns.local.
                     RNacRPSW6hxhtze6sV6LafcOIuqMex1TBVo6FbI1pkXV
                     ZVz3Bm+xWuo7QUP74I8r7j5y8cPUpRNRMOO6StAr73Wy
                     GGgb7HExUSHTKU03xHAwnAvKbcTlXkJcSHmxBrY+/yE+
                     HkbwFHe4Y9qNTF4ygvU0BtObbJ3+e0hW8wr6YIU= )

The signatures are cached, so this does not turn into an easy DDoS at once.

Other responses you expect from a DNS server are supported, like SOA, NS, TXT, etc.

Tagged , , , ,

Learning Go

"Learning Go" is a book that gives an introduction into the Go language of Google. It is licensed under a copy-left license. The book currently consists out +/- 120 (A4 sized) pages and the following chapters:

  1. Introduction
    Show how to install Go and details the lineage of the language Go.
  2. Basics
    Types, variables and control structures.
  3. Functions
    How to make and use functions.
  4. Packages
    Functions and data is grouped together in packages. Here you will see how to make your own package. How to unit test your package is also described.
  5. Beyond the basics
    Learn how to create your own data types and define function on them (called methods in Go).
  6. Interfaces
    Go does not support Object Orientation in the traditional sense. In Go the central concept is interfaces.
  7. Concurrency
    With the go keyword function can be started in separate routines (called goroutines). Communication with those goroutines is done via channels.
  8. Communication
    How to create/read/write from and to files. And how to do networking.

Each chapter concludes with a number of exercises (and answers) to may help you to get some hands on experience. Currently it has more than 30 exercises.

There is also a Chinese translation by Mike Spook.

What readers say:

I am really glad that I found your Go book. It's been a couple of weeks since I started learning Go, but didn't make much progress till I found your book.

I also read with great interest the (successive versions of the) free E-book by Miek Gieben & Co. Which I find definitely very well crafted and very useful. Definitely an extremely laudable initiative.

Prebuild PDFs can be found at /downloads/Go. The source code of the book can be found on github.

It is written in LaTeX with the Memoir class (and a bunch a extra classes).

Questions, patches, text, bug reports and general discussions can be directed to miek@miek.nl (in English or Dutch). If you like this work you may choose support it by sending me money :)

Tagged , , ,

DNS Router

Say you have a zone that does not fit in the memory of one machine. Who hasn't these zones nowadays? How would you solve such a problem? With a DNS router of course!

Dns router is a small Go program I whipped together that acts as a DNS router. Clients register an <ip:port, regexp> combination and will then only receive queries that match that regular expression. The registration happens in Etcd. Of course "Dns router" (I need a better name), has some features, it will:

  • health checks the server every 5 seconds using TCP using a id.server. TXT CH query;
  • set an Ectd watch to get updates when a new server is added or removed.

So it's pretty dynamic, but the health checking could be better, as servers will never be re-added once removed.

Ldns actually has an utility to split a zonefile into chunks (with a new SOA, called zsplit, see http://git.nlnetlabs.nl/ldns/tree/examples/ldns-zsplit.1. In this case I just manually split a zone into 2 chunks, one with names starting with [ab] and another with [cd]. Of course the apex of the zone needs to go somewhere, so this has to be specified somewhere. See the examples later in this article.

For the purpose of this article I've used 2 docker images with BIND9 and the 2 (split) zones I have prepared.

The whole "how-do-I-prepare-an-Docker-image" will be left out, there is plenty of documentation on the Net on this. In all I've created two docker images, two running BIND9 and pieces of miek.nl. After fiddling with docker I found the following command line would start my VMs OK:

docker run -p 5300:53/udp -p 5300:53 -d miek/bind:bind9a

And run the other docker container on a different port:

docker run -p 5301:53/udp -p 5301:53 -d miek/bind:bind9c

So, all a-b names are reachable on port 5300 and all c-d names can be found via port 5301.

Assuming we have an etcd running on our host we register our two docker VMs with it and then start dnsrouter.

curl -L http://127.0.0.1:4001/v2/keys/dnsrouter/a1 -XPUT -d value="127.0.0.1:5300,^[ab]\.miek"
curl -L http://127.0.0.1:4001/v2/keys/dnsrouter/c1 -XPUT -d value="127.0.0.1:5301,^[cd]\.miek"

And two routes for the apex of the zone, dnsrouter will round robin between the two servers.

% DNS_ADDR=127.0.0.1:5299 ./dnsrouter
2014/05/17 10:54:38 enabling health checking
2014/05/17 10:54:38 setting watch
2014/05/17 10:54:38 getting initial list
2014/05/17 10:54:38 unable to parse node /dnsrouter with value # small bug I need to fix
2014/05/17 10:54:38 adding route ^[ab]\.miek for 127.0.0.1:5300
2014/05/17 10:54:38 adding route ^[cd]\.miek for 127.0.0.1:5301
2014/05/17 10:54:38 ready for queries

So dnsrouter is running on port 5299, lets try some queries and check the logs of dnsrouter.

% dig @localhost +noall +ans -p 5299 TXT a.miek.nl
a.miek.nl.      43200   IN  TXT "aa"
% dig @localhost +noall +ans -p 5299 TXT c.miek.nl
c.miek.nl.      43200   IN  TXT "cc"

And the logs from dnsrouters:

2014/05/17 11:04:07 routing a.miek.nl. to 127.0.0.1:5300
2014/05/17 11:04:12 routing c.miek.nl. to 127.0.0.1:5301

A request for the apex of the zone fails because we don't have setup a route for it, so let's add two:

2014/05/17 11:06:51 adding route ^miek for 127.0.0.1:5301
2014/05/17 11:06:58 adding route ^miek for 127.0.0.1:5300

And dig again:

% dig @localhost +noall +ans -p 5299 SOA miek.nl
miek.nl. 43200   IN  SOA linode.atoom.net. miek.miek.nl. 1282630056 14400 3600 604800 86400

And we even see some round robin at work:

2014/05/17 11:07:23 routing miek.nl. to 127.0.0.1:5300
2014/05/17 11:07:23 routing miek.nl. to 127.0.0.1:5301

Let's kill one of the docker VMs. Dnsrouter should detect this and disable that server. It does not autmatically re-add it, for that you need to write again to etcd, which will then automatically be picked up by Dnsrouter.

% docker stop 29fde54f64f8
...
2014/05/17 11:22:00 healthcheck failed for 127.0.0.1:5300
2014/05/17 11:22:00 removing 127.0.0.1:5300

And starting it again:

% docker start 29fde54f64f8
% curl -L http://127.0.0.1:4001/v2/keys/dnsrouter/a1 -XPUT -d value="127.0.0.1:5300,^[ab]\.miek"
...
2014/05/17 11:23:41 adding route ^[ab]\.miek for 127.0.0.1:5300

In an upcoming article I will describe how I got this running on CoreOS.

Tagged , ,

grub_term_highlight_color

During two Ubuntu 14.04 upgrades, both on a Mac (so needing an EFI boot), grub was borked after the install resulting in a grub rescue prompt when booting.

The actual error was error: symbol 'grub_term_highlight_color' not found.

Needless to say I couldn't get the system to boot from this prompt.

I had a Fedora boot USB stick laying around, but using that did not really fix the problem, in any case I could use it to copy off /home if I could not rescue the system.

HOWEVER, the following procedure worked great!

  • Download super grub disk;
    • 1.4 MB download!
  • Copy it to an USB stick: dd if=super_grub_disk_hybrid-1.98s1.iso of=/dev/sdb;
  • Boot from this USB stick;
  • In the boot menu, choose detect OSs;
  • Boot you newly minted, unbootable, Ubuntu 14.04;
  • If all goes well it should boot Ubuntu and you can log in;
  • Install and use boot repair;
  • If you use EFI, check this: https://help.ubuntu.com/community/UEFI.
Tagged , ,

i3

GNOME 3 finally pushed me over the edge. After I brief stint with cinnamon, I decided the only thing left was to configure a tiling window manager and some tweaks to make it more usable. For no reason at all, I settled on i3, which seems really nice and simple to configure.

But how to use i3 comfortable? i3 is a tiling window manager, which makes it ubercool, but with it you loose things like automount, brightness keys, etc.; all the things you expect from a Linux desktop nowadays. This blog item deals with getting the goodies from i3, without giving up on all the other things you like.

See http://i3wm.org/docs/user-contributed/lzap-config.html, and the Arch Wiki for some good documentation too https://wiki.archlinux.org/index.php/I3 to get this going.

I took the following steps to get it going, this is still rough around the edges, because I'm lazy. See this screenshot: i3 comfy screenshot

  • gnome-session-daemon
    1. Start this daemon, it makes stuff much easier, like default keys for brightness, etc.
    2. Also makes your gtk-apps look nice, because they are themed.
  • Keys
    1. Volume - works with gnome-session-daemon.
    2. Keyboard - idem.
    3. Screen - idem.
  • Locking

    1. i3lock
    2. When running gnome-settings-daemon, disable the screensaver:

      gconftool-2 --type boolean -s /apps/gnome_settings_daemon/screensaver/start_screensaver false

    3. Create a keycombo in your config to lock your screen, i3lock -i <image.png> -t can be used for that.

    4. And better yet: sudo apt-get remove gnome-screensaver
  • Network manager

    1. Start nm-applet.
  • Bluetooth
    1. apt-get install blueman, it gives you blueman-applet.
  • Notification
    1. There is dunst, but this was also fixed by running gnome-settings-daemon, but needs some extra configuration for automounting notifications.
  • Mounting external disks with notifications.

    1. Install the debian package of udisks-glue (64 bit) from: https://packages.debian.org/sid/amd64/udisks-glue/download
    2. Add some config for notifications: start with: udisks-glue -c <file> and in that config file add or set:
      match disks {
          post_insertion_command = "udisks --mount %device_file --mount-options sync"
          post_mount_command = "notify-send %device_file 'mounted %device_file %mount_point'"
          post_unmount_command = "notify-send %device_file 'unmounted %device_file %mount_point'"
      }
      
  • puvacontrol for sound stuff other than volume control

    1. Add a shortcut that starts pavucontrol.
  • Solarized colors: https://github.com/lkraav/dotfiles/tree/master/.i3, which you can then tweak, to make them even better.
  • Lock screen when suspending:
    1. exec --no-startup-id xautolock -detectsleep -locker 'i3lock -i ~/.backgrounds/eunight2_pv_big.png -t'
    2. Actually this patch http://searchcode.com/codesearch/raw/40065437 is what you want.
    3. Actually you this is a patch that applies cleanly: https://gist.github.com/miekg/9430422

Config file

~/.i3/config

Add the following lines, to implement some of the above.

exec --no-startup-id /usr/bin/gnome-settings-daemon
exec --no-startup-id nm-applet
exec --no-startup-id blueman-applet
exec --no-startup-id udisks-glue
exec --no-startup-id export $(gnome-keyring-daemon)
exec --no-startup-id feh --bg-scale ~/.backgrounds/Early_Morning_by_Robert_Katzki.jpg

Dmenu

I had some truetype font problems with demnu, this was fix by:

~/.i3/dmenu_run -b -fn 'Source Code Pro-9', basically use dmenu.xft instead of dmenu, fix dmenu_run for this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/bin/sh
cachedir=${XDG_CACHE_HOME:-"$HOME/.cache"}
if [ -d "$cachedir" ]; then
        cache=$cachedir/dmenu_run
else
        cache=$HOME/.dmenu_cache # if no xdg dir, fall back to dotfile in ~
fi
(
        IFS=:
        if stest -dqr -n "$cache" $PATH; then
                stest -flx $PATH | sort -u | tee "$cache" | dmenu.xft "$@"
        else
                dmenu.xft "$@" < "$cache"
        fi
) | ${SHELL:-"/bin/sh"} &
Tagged , ,

New Site Based on Pelican

I've revamped http://miek.nl it is now using Pelican, instead of nanoblogger. The old miek.nl site is available via http://archive.miek.nl.

I'm in the progress of migration articles over. I still need to think what I want to do with my project pages, because most stuff lives on on https://github.com/miekg.

Tagged

Personal Go install

With golang 1.2, both godoc and go vet are removed from the main repository. Here is a super short list of commands (that I personally use) to install Go and both tools.

export GOBIN=~/bin
export GOPATH=~/g
mkdir -p ~/upstream
mkdir -p ~/g
cd ~/upstream
hg clone https://code.google.com/p/go/
hg update release
cd src
./all.bash
go get code.google.com/p/go.tools/cmd/godoc
go get code.google.com/p/go.tools/cmd/vet
go get code.google.com/p/go.tools/cmd/cover
Tagged