Skip to content

Commit

Permalink
adding duration config option for automated renovations
Browse files Browse the repository at this point in the history
  • Loading branch information
Eduardo committed Jul 26, 2020
1 parent 2f127bf commit dd6cbbf
Show file tree
Hide file tree
Showing 17 changed files with 421 additions and 184 deletions.
122 changes: 77 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
# DNS Tools: ZONEMD digest calculator and Signer (using PKCS11 and files)


[![Go Report Card](https://goreportcard.com/badge/github.com/niclabs/dns-tools)](https://goreportcard.com/report/github.com/niclabs/dns-tools) [![Build Status](https://travis-ci.org/niclabs/dns-tools.svg?branch=master)](https://travis-ci.org/niclabs/dns-tools)


## How to build dns-tools

The following libraries should be installed in the systems which are going to use the compiled library:

* git
* gcc
* Go (1.12.3 or higher)
- git
- gcc
- Go (1.12.3 or higher)

On [Debian 10 (Buster)](https://www.debian.org), with a sudo-enabled user, the commands to run to install dependencies and
On [Debian 10 (Buster)](https://www.debian.org), with a sudo-enabled user, the commands to run to install dependencies and
build are the following:

```bash
Expand All @@ -22,7 +20,7 @@ sudo apt install build-essential pkg-config git

To compile it, you need to have `Go` installed on your machine. You can find how to install Go on [its official page](https://golang.org/doc/install).

Then, you need to clone, execute and build the repository:
Then, you need to clone, execute and build the repository:

```
git clone https://github.com/niclabs/dns-tools --branch v1.1.0
Expand All @@ -35,35 +33,40 @@ The file `dns-tools` will be created on the same directory.
## Command Flags

the command has three modes:
* **Verify** `dns-tools verify` allows to verify a previously signed and/or digested zone. It only receives one parameter, `--file (-f)`, that is used as the input file for verification.
* **Reset PKCS#11 Keys** `dns-tools reset-pkcs11-keys` Deletes all the keys from the HSM. Is a very dangerous command. It uses some parameters from `sign`, as `-p`, `-l` and `-k`.
* **Sign** allows to sign a zone. Its common parameters are:
* `--create-keys (-c)` creates the keys if they doesn't exist.
* `--zsk-expiration-date (-Z)` Allows to use a specific expiration date for RRSIG signatures with ZSK key.
* `--ksk-expiration-date (-K)` Allows to use a specific expiration date for RRSIG signatures with KSK key.
* `--file (-f)` allows to select the file that will be signed.
* `--nsec3 (-3)` Uses NSEC3 for zone signing, as specified in [RFC5155](https://tools.ietf.org/html/rfc5155). If not activated, it uses NSEC.
* `--optout (-o)` Uses Opt-out, as specified in [RFC5155](https://tools.ietf.org/html/rfc5155).
* `--p11lib (-p)` selects the library to use as pkcs11 HSM driver.
* `--sign-algorithm (-a)` Sign algorithm used. It can be 'rsa' or 'ecdsa'.
* `--zone (-z)` Zone name
* `--digest (-d)` If true, the signature also creates a [Digest](https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-05.html) over the zone
- `--info (-i)` Add a TXT RR to the zone with signing information (signer software, mode and library used if PKCS#11)
* **ZONEMD calculation** Allows to generate a [ZONEMD](https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-05.html) RR over the zone. It allows the following commands:

- **Verify** `dns-tools verify` allows to verify a previously signed and/or digested zone. It only receives one parameter, `--file (-f)`, that is used as the input file for verification.
- **Reset PKCS#11 Keys** `dns-tools reset-pkcs11-keys` Deletes all the keys from the HSM. Is a very dangerous command. It uses some parameters from `sign`, as `-p`, `-l` and `-k`.
- **Sign** allows to sign a zone. Its common parameters are:
- `--create-keys (-c)` creates the keys if they doesn't exist.
- `--zsk-expiration-date` Allows to use a specific expiration date for ZSK key if it is created. It can be overrided by --zsk-duration.
- `--ksk-expiration-date` Allows to use a specific expiration date for KSK key if it is created. It can be overrided by --ksk-duration.
- `--rrsig-expiration-date` Allows to use a specific expiration date for RRSIG signatures. It can be overrided by --rrsig-duration.
- `--zsk-duration` Allows to use an expiration date for ZSK key relative to current time if it is created. It overrides --zsk-expiration-date. Default value is empty.
- `--ksk-duration` Allows to use an expiration date for KSK key relative to current time if it is created. It overrides --ksk-expiration-date. Default value is empty.
- `--rrsig-duration` Allows to use a expiration date for RRSIG signatures relative to current time. It overrides --rrsig-expiration-date. Default value is empty.
- `--file (-f)` allows to select the file that will be signed.
- `--nsec3 (-3)` Uses NSEC3 for zone signing, as specified in [RFC5155](https://tools.ietf.org/html/rfc5155). If not activated, it uses NSEC.
- `--optout (-o)` Uses Opt-out, as specified in [RFC5155](https://tools.ietf.org/html/rfc5155).
- `--p11lib (-p)` selects the library to use as pkcs11 HSM driver.
- `--sign-algorithm (-a)` Sign algorithm used. It can be 'rsa' or 'ecdsa'.
- `--zone (-z)` Zone name
- `--digest (-d)` If true, the signature also creates a [Digest](https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-05.html) over the zone
* `--info (-i)` Add a TXT RR to the zone with signing information (signer software, mode and library used if PKCS#11)
- **ZONEMD calculation** Allows to generate a [ZONEMD](https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-05.html) RR over the zone. It allows the following commands:
- `--file (-f)` Input zone file
- `--output (-o)` Output for zone file
- `--output (-o)` Output for zone file
- `--info (-i)` Add a TXT RR to the zone with signing information (signer software, mode and library used if PKCS#11)

## Signing modes

Sign can be used in two modes:
* **PKCS#11**: `dns-tools sign pkcs11` connects to a PKCS#11 enabled device to sign the zone. It considers the following options:
- `--key-label (-l)` allows to choose a label for the created keys (if not, they will have dns-tools as name).
- `--user-key (-k)` HSM key, if not specified, the default key used is `1234`.
* **File**: `dns-tools sign file` uses two PEM files with PKCS#8 encoded keys. It requires to define two options:
- `--zsk-file (-Z)` ZSK PEM File location. If `--create-keys` is enabled, the file will be created and any previous key will be overriden, so use it with care.
- `--ksk-file (-K)` KSK PEM File location. If `--create-keys` is enabled, the file will be created and any previous key will be overriden, so use it with care.

- **PKCS#11**: `dns-tools sign pkcs11` connects to a PKCS#11 enabled device to sign the zone. It considers the following options:
- `--key-label (-l)` allows to choose a label for the created keys (if not, they will have dns-tools as name).
- `--user-key (-k)` HSM key, if not specified, the default key used is `1234`.
- **File**: `dns-tools sign file` uses two PEM files with PKCS#8 encoded keys. It requires to define two options:
- `--zsk-file (-Z)` ZSK PEM File location. If `--create-keys` is enabled, the file will be created and any previous key will be overriden, so use it with care.
- `--ksk-file (-K)` KSK PEM File location. If `--create-keys` is enabled, the file will be created and any previous key will be overriden, so use it with care.

### Using a PKCS#11 device

Expand Down Expand Up @@ -101,7 +104,7 @@ The following command creates an output file with a ZONEMD RR:

## How to delete PKCS11 keys

The following command removes the created keys with an specific tag, using the [DTC](https://github.com/niclabs/dtc) library
The following command removes the created keys with an specific tag, using the [DTC](https://github.com/niclabs/dtc) library

```
./dns-tools reset-pkcs11-keys -p ./dtc.so
Expand All @@ -112,35 +115,64 @@ The following command removes the created keys with an specific tag, using the
You can create a json config file with the structure of `config.sample.json` to set the variables.
The config file will be looked for at the following locations:

* `/etc/dns-tools/dns-tools-config.json`
* `./dns-tools-config.json` (Current location)
- `/etc/dns-tools/dns-tools-config.json`
- `./dns-tools-config.json` (Current location)

You can also set the config file path using `--config` flag.

### Duration format

The fields `--{zsk,ksk,rrsig}-duration` are parsed using the following regular expression:

`(<n> <time_keyword>)(,? +<n> <time_keyword>)*`

Where:

- `<n>` corresponds to a non-negative integer number.
- `<time_keyword>` is a value in the following list:
- `s`, `sec`, `secs`, `seconds` for seconds.
- `min`, `minute`, `mins`, `minutes` for minutes
- `h`, `hr`, `hour`, `hrs`, `hours` for hours
- `w`, `week`, `weeks` for weeks
- `m`, `month`, `months` for months
- `y`, `year`, `years` for years


In other words, the duration units are separated by one or more spaces, ignoring plurals and commas at the end of each duration definition.

The relative duration definition is used with the time that the command is executed.

Examples:

* 1 year 3 months
* 1 hour 3 seconds
* 3 weeks 2 months 4 seconds 1 year

## Features

- [x] Read zone
- [x] Parse zone
- [x] Create keys in HSM
- [x] Sign using PKCS11 (for HSMs):
- [x] RSA
- [x] ECDSA
- [ ] SHA-1
- [ ] SHA128
- [x] SHA256
- [ ] SHA512
- [x] RSA
- [x] ECDSA
- [ ] SHA-1
- [ ] SHA128
- [x] SHA256
- [ ] SHA512
- [x] Sign using PKCS#8-encoded PEM keys:
- [x] RSA
- [x] ECDSA
- [ ] SHA-1
- [ ] SHA128
- [x] SHA256
- [ ] SHA512
- [x] RSA
- [x] ECDSA
- [ ] SHA-1
- [ ] SHA128
- [x] SHA256
- [ ] SHA512
- [x] Calculate ZONEMD RRs
- [x] Verify signed/digested zones
- [x] Reuse keys
- [x] Delete keys
- [x] Save zone to file

## Bugs
* [Some incompatibilities with some common PKCS11-enabled libraries](https://github.com/niclabs/dns-tools/issues/8)

- [Some incompatibilities with some common PKCS11-enabled libraries](https://github.com/niclabs/dns-tools/issues/8)
54 changes: 45 additions & 9 deletions cmd/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@ package cmd

import (
"fmt"
"github.com/niclabs/dns-tools/tools"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"os"
"path/filepath"
"strings"
"time"

"github.com/niclabs/dns-tools/tools"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

// Defaults for ZSK and KSK signature expirations
var (
DefaultKSKExpiration time.Time = time.Now().AddDate(1, 0, 0)
DefaultZSKExpiration time.Time = time.Now().AddDate(1, 0, 0)
DefaultRRSigExpiration time.Time = time.Now().AddDate(0, 3, 0)
)

func init() {
Expand All @@ -18,11 +27,16 @@ func init() {
signCmd.PersistentFlags().StringP("sign-algorithm", "a", "rsa", "Algorithm used in signing.")
signCmd.PersistentFlags().BoolP("nsec3", "3", false, "Use NSEC3 instead of NSEC.")
signCmd.PersistentFlags().BoolP("opt-out", "x", false, "Use NSEC3 with opt-out.")
signCmd.PersistentFlags().StringP("zsk-expiration-date", "Z", "", "ZSK Signature expiration Date, in YYYYMMDD format. Default is one month from now.")
signCmd.PersistentFlags().StringP("ksk-expiration-date", "K", "", "KSK Signature expiration Date, in YYYYMMDD format. Default is three months from now.")
signCmd.PersistentFlags().BoolP("digest", "d", false, "If true, DigestEnabled RR is added to the signed zone")
signCmd.PersistentFlags().BoolP("info", "i", false, "If true, an TXT RR is added with information about the signing process (tool and mode)")

signCmd.PersistentFlags().String("zsk-expiration-date", "", "ZSK Key expiration Date, in YYYYMMDD format. It is ignored if --zsk-duration is set. Default is three months from now.")
signCmd.PersistentFlags().String("ksk-expiration-date", "", "KSK Key expiration Date, in YYYYMMDD format. It is ignored if --ksk-duration is set. Default is one year from now.")
signCmd.PersistentFlags().String("rrsig-expiration-date", "", "RRSIG expiration Date, in YYYYMMDD format. It is ignored if --ksk-duration is set. Default is three months from now.")
signCmd.PersistentFlags().String("zsk-duration", "", "Relative ZSK Key expiration Date, in human readable format (combining numbers with labels like year(s), month(s), day(s), hour(s), minute(s), second(s)). Overrides --ksk-date-expiration. Default is empty.")
signCmd.PersistentFlags().String("ksk-duration", "", "Relative KSK Key expiration Date, in human readable format (combining numbers with labels like year(s), month(s), day(s), hour(s), minute(s), second(s)). Overrides --zsk-date-expiration. Default is empty.")
signCmd.PersistentFlags().String("rrsig-duration", "", "Relative RRSIG expiration Date, in human readable format (combining numbers with labels like year(s), month(s), day(s), hour(s), minute(s), second(s)). Overrides --rrsig-date-expiration. Default is empty.")

pkcs11Cmd.PersistentFlags().StringP("user-key", "k", "1234", "HSM User Login PKCS11Key.")
pkcs11Cmd.PersistentFlags().StringP("key-label", "l", "HSM-tools", "Label of HSM Signer PKCS11Key.")
pkcs11Cmd.PersistentFlags().StringP("p11lib", "p", "", "Full path to PKCS11Type lib file.")
Expand Down Expand Up @@ -148,8 +162,7 @@ func newSignConfig() (*tools.ContextConfig, error) {

path := viper.GetString("file")
out := viper.GetString("output")
zskExpDateStr := viper.GetString("zsk-expiration-date")
kskExpDateStr := viper.GetString("zsk-expiration-date")

signAlgorithm := viper.GetString("sign-algorithm")

if len(path) == 0 {
Expand All @@ -168,17 +181,40 @@ func newSignConfig() (*tools.ContextConfig, error) {
return nil, err
}

kskExpDate, err := getExpDate(viper.GetString("zsk-duration"), viper.GetString("zsk-expiration-date"), DefaultZSKExpiration)
if err != nil {
return nil, err
}
zskExpDate, err := getExpDate(viper.GetString("ksk-duration"), viper.GetString("ksk-expiration-date"), DefaultKSKExpiration)
if err != nil {
return nil, err
}
rrsigExpDate, err := getExpDate(viper.GetString("rrsig-duration"), viper.GetString("rrsig-expiration-date"), DefaultRRSigExpiration)
if err != nil {
return nil, err
}
return &tools.ContextConfig{
Zone: zone,
CreateKeys: createKeys,
NSEC3: nsec3,
DigestEnabled: digest,
OptOut: optOut,
SignAlgorithm: signAlgorithm,
KSKExpDateStr: kskExpDateStr,
ZSKExpDateStr: zskExpDateStr,
KSKExpDate: kskExpDate,
ZSKExpDate: zskExpDate,
RRSIGExpDate: rrsigExpDate,
FilePath: path,
OutputPath: out,
Info: info,
}, nil
}

func getExpDate(durString, expDate string, def time.Time) (time.Time, error) {
if len(durString) > 0 {
return tools.DurationToTime(time.Now(), durString)
}
if len(expDate) > 0 {
return time.Parse("20060102", expDate)
}
return def, nil
}
4 changes: 4 additions & 0 deletions dns-tools-config.sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
"digest": true,
"zsk-expiration-date": "20300101",
"ksk-expiration-date": "20300101",
"rrsig-expiration-date": "20300101",
"zsk-duration": "1 year 7 months 2 days 8 hours 3 minutes 12 seconds",
"ksk-duration": "10 years 3 months 2 days 1 hr 4 min 3 secs",
"rrsig-duration": "10 years 3 months 2 days 1 hr 4 min 3 secs",
"zone": "example.com.",
"zsk-keyfile": "zsk.pem",
"ksk-keyfile": "ksk.pem",
Expand Down
53 changes: 17 additions & 36 deletions tools/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import (
// Context contains the state of a zone signing process.
type Context struct {
Config *ContextConfig
KSKExpDate time.Time // Expiration date for signatures with KSK Key.
ZSKExpDate time.Time // Expiration date for signatures with ZSK Key.
File io.Reader // zone path
Output io.WriteCloser // Out path
rrs RRArray // rrs
Expand All @@ -30,17 +28,18 @@ type Context struct {

// ContextConfig contains the common args to sign and verify files
type ContextConfig struct {
Zone string // Zone name
CreateKeys bool // If True, the sign process creates new keys for the signature.
NSEC3 bool // If true, the zone is signed using NSEC3
OptOut bool // If true and NSEC3 is true, the zone is signed using OptOut NSEC3 flag.
DigestEnabled bool // If true, the zone is hashed and DigestEnabled is used
SignAlgorithm string // Signature algorithm
FilePath string // Output Path
OutputPath string // Output Path
KSKExpDateStr string // KSK-signed Signature Expiration Date in String
ZSKExpDateStr string // ZSK-signed Signature Expiration Date in String
Info bool // If true, a credits txt will be added to _dnstools subdomain.
Zone string // Zone name
CreateKeys bool // If True, the sign process creates new keys for the signature.
NSEC3 bool // If true, the zone is signed using NSEC3
OptOut bool // If true and NSEC3 is true, the zone is signed using OptOut NSEC3 flag.
DigestEnabled bool // If true, the zone is hashed and DigestEnabled is used
SignAlgorithm string // Signature algorithm
FilePath string // Output Path
OutputPath string // Output Path
KSKExpDate time.Time // KSK Key Expiration Date
ZSKExpDate time.Time // ZSK Key Expiration Date
RRSIGExpDate time.Time // RRSIG Expiration Date
Info bool // If true, a credits txt will be added to _dnstools subdomain.
}

// NewContext creates a new context based on a configuration structure. It also receives
Expand All @@ -50,8 +49,6 @@ func NewContext(config *ContextConfig, log *log.Logger) (ctx *Context, err error
ctx = &Context{
Config: config,
Log: log,
KSKExpDate: time.Now().AddDate(0, 3, 0),
ZSKExpDate: time.Now().AddDate(0, 1, 0),
Output: os.Stdout,
SignAlgorithm: algorithm,
}
Expand All @@ -63,22 +60,6 @@ func NewContext(config *ContextConfig, log *log.Logger) (ctx *Context, err error
}
}

if len(config.KSKExpDateStr) > 0 {
parsedDate, err := time.Parse("20060102", config.KSKExpDateStr)
if err != nil {
return nil, fmt.Errorf("cannot parse expiration date: %s", err)
}
ctx.KSKExpDate = parsedDate
}

if len(config.ZSKExpDateStr) > 0 {
parsedDate, err := time.Parse("20060102", config.ZSKExpDateStr)
if err != nil {
return nil, fmt.Errorf("cannot parse expiration date: %s", err)
}
ctx.ZSKExpDate = parsedDate
}

if len(config.OutputPath) > 0 {
writer, err := os.Create(config.OutputPath)
if err != nil {
Expand Down Expand Up @@ -190,23 +171,23 @@ func (ctx *Context) AddNSEC13() {
func (ctx *Context) NewPKCS11Session(key, label, p11lib string) (SignSession, error) {
p := pkcs11.New(p11lib)
if p == nil {
return nil, fmt.Errorf("Error initializing %s: file not found\n", p11lib)
return nil, fmt.Errorf("Error initializing %s: file not found", p11lib)
}
err := p.Initialize()
if err != nil {
return nil, fmt.Errorf("Error initializing %s: %s. (Has the .db RW permission?)\n", p11lib, err)
return nil, fmt.Errorf("Error initializing %s: %s. (Has the .db RW permission?)", p11lib, err)
}
slots, err := p.GetSlotList(true)
if err != nil {
return nil, fmt.Errorf("Error checking slots: %s\n", err)
return nil, fmt.Errorf("Error checking slots: %s", err)
}
session, err := p.OpenSession(slots[0], pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
if err != nil {
return nil, fmt.Errorf("Error creating session: %s\n", err)
return nil, fmt.Errorf("Error creating session: %s", err)
}
err = p.Login(session, pkcs11.CKU_USER, key)
if err != nil {
return nil, fmt.Errorf("Error login with provided key: %s\n", err)
return nil, fmt.Errorf("Error login with provided key: %s", err)
}
return &PKCS11Session{
libPath: p11lib,
Expand Down
Loading

0 comments on commit dd6cbbf

Please sign in to comment.