1
0
Fork 0
mirror of https://github.com/SamTherapy/dnscrypt.git synced 2024-07-02 21:56:06 +00:00

Add convert-dnscrypt-wrapper command

The new command allows to convert keys generated by dnscrypt-wrapper to a configuration compatible with the "dnscrypt" tool
This commit is contained in:
sdbochkarev 2021-02-02 13:54:52 +03:00 committed by GitHub
parent 053da1d64f
commit d0ae1d198d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 179 additions and 34 deletions

View file

@ -11,6 +11,8 @@ This repo includes everything you need to work with DNSCrypt. You can run your o
* [Command-line tool](#commandline)
* [How to install](#install)
* [How to configure](#configure)
* [Converting dnscrypt-wrapper configuration](#convertfromwrapper)
* [Running a server](#runningserver)
* [Making lookups](#lookup)
* [Programming interface](#api)
@ -28,12 +30,19 @@ Please note, that even though this tool can work as a server, it's purpose is me
Download and unpack an archive for your platform from the [latest release](https://github.com/ameshkov/dnscrypt/releases).
### <a id="runningserver"></a> Running a server
### <a id="configure"></a> How to configure
Generate a configuration file for running a DNSCrypt server:
```shell script
./dnscrypt generate --provider-name=2.dnscrypt-cert.example.org --out=config.yaml
```
./dnscrypt generate
[generate command options]
-p, --provider-name= DNSCrypt provider name. Param is required.
-o, --out= Path to the resulting config file. Param is required.
-k, --private-key= Private key (hex-encoded)
-t, --ttl= Certificate time-to-live (seconds)
```
It will generate a configuration file that looks like this:
@ -54,10 +63,34 @@ certificate_ttl: 0s
* `es_version` - crypto to use. Can be `1` (XSalsa20Poly1305) or `2` (XChacha20Poly1305).
* `certificate_ttl` - certificate time-to-live. By default it's set to `0` and in this case 1-year cert is generated. The certificate is generated on `dnscrypt` start-up and it will only be valid for the specified amount of time. You should periodically restart `dnscrypt` to rotate the cert.
#### <a id="convertfromwrapper"></a> Converting [dnscrypt-wrapper](https://github.com/cofyc/dnscrypt-wrapper) configuration
Also, to create a configuration, you can use the keys generated using [dnscrypt-wrapper](https://github.com/cofyc/dnscrypt-wrapper) by running the command:
```
./dnscrypt convert-dnscrypt-wrapper
[convert-dnscrypt-wrapper command options]
-p, --private-key= Path to the DNSCrypt resolver private key file that is used for signing certificates. Param is required.
-r, --resolver-secret= Path to the Short-term privacy key file for encrypting/decrypting DNS queries. If not specified, resolver_secret and resolver_public will be randomly generated.
-n, --provider-name= DNSCrypt provider name. Param is required.
-o, --out= Path to the resulting config file. Param is required.
-t, --ttl= Certificate time-to-live (seconds)
```
### <a id="runningserver"></a> Running a server
This configuration file can be used to run a DNSCrypt forwarding server:
```shell script
./dnscrypt server --config=config.yaml --forward=94.140.14.140:53
```
./dnscrypt server
[server command options]
-c, --config= Path to the DNSCrypt configuration file. Param is required.
-f, --forward= Forwards DNS queries to the specified address (default: 94.140.14.140:53)
-l, --listen= Listening addresses (default: 0.0.0.0)
-p, --port= Listening ports (default: 443)
```
Now you can go to https://dnscrypt.info/stamps and use `provider_name` and `public_key` from this configuration to generate a DNS stamp. Here's how it looks like for a server running on `127.0.0.1:443`:
@ -71,26 +104,27 @@ sdns://AQcAAAAAAAAADTEyNy4wLjAuMTo0NDMg8R3bzEgX5UOEX93Uy4gYSbZCJvPeOXYlZp2HuRm8T
You can use that stamp to send a DNSCrypt request to your server:
```
./dnscrypt lookup-stamp \
--stamp=sdns://AQcAAAAAAAAADTEyNy4wLjAuMTo0NDMg8R3bzEgX5UOEX93Uy4gYSbZCJvPeOXYlZp2HuRm8T7AbMi5kbnNjcnlwdC1jZXJ0LmV4YW1wbGUub3Jn \
--domain=example.org \
--type=a
./dnscrypt lookup-stamp
[lookup-stamp command options]
-n, --network= network type (tcp/udp) (default: udp)
-s, --stamp= DNSCrypt resolver stamp. Param is required.
-d, --domain= Domain to resolve. Param is required.
-t, --type= DNS query type (default: A)
```
You can also send a DNSCrypt request using a command that does not require stamps:
```
./dnscrypt lookup \
--provider-name=2.dnscrypt-cert.opendns.com \
--public-key=b7351140206f225d3e2bd822d7fd691ea1c33cc8d6668d0cbe04bfabca43fb79 \
--addr=208.67.220.220 \
--domain=example.org \
--type=a
```
In both cases, you can specify the transport using the 'network' flag (`udp` default)
```
./dnscrypt lookup-stamp --network={tcp|udp} ...
[lookup command options]
-n, --network= network type (tcp/udp) (default: udp)
-p, --provider-name= DNSCrypt resolver provider name. Param is required.
-k, --public-key= DNSCrypt resolver public key. Param is required.
-a, --addr= Resolver address (IP[:port]). By default, the port is 443. Param is required.
-d, --domain= Domain to resolve. Param is required.
-t, --type= DNS query type (default: A)
```
## <a id="api"></a> Programming interface

View file

@ -0,0 +1,108 @@
package main
import (
"crypto/ed25519"
"fmt"
"io/ioutil"
"time"
"github.com/AdguardTeam/golibs/log"
"github.com/ameshkov/dnscrypt/v2"
"golang.org/x/crypto/curve25519"
"gopkg.in/yaml.v3"
)
// ConvertWrapperArgs - "convert-dnscrypt-wrapper" command arguments
type ConvertWrapperArgs struct {
PrivateKeyFile string `short:"p" long:"private-key" description:"Path to the DNSCrypt resolver private key file that is used for signing certificates. Param is required." required:"true"`
ResolverSkFile string `short:"r" long:"resolver-secret" description:"Path to the Short-term privacy key file for encrypting/decrypting DNS queries. If not specified, resolver_secret and resolver_public will be randomly generated."`
ProviderName string `short:"n" long:"provider-name" description:"DNSCrypt provider name. Param is required." required:"true"`
Out string `short:"o" long:"out" description:"Path to the resulting config file. Param is required." required:"true"`
CertificateTTL int `short:"t" long:"ttl" description:"Certificate time-to-live (seconds)"`
}
// convertWrapper - generates DNSCrypt configuration from both dnscrypt and server private keys
func convertWrapper(args ConvertWrapperArgs) {
log.Info("Generating configuration for %s", args.ProviderName)
var rc = dnscrypt.ResolverConfig{
EsVersion: dnscrypt.XSalsa20Poly1305,
CertificateTTL: time.Duration(args.CertificateTTL) * time.Second,
ProviderName: args.ProviderName,
}
// make PrivateKey
var privateKey ed25519.PrivateKey
privateKey = getFileContent(args.PrivateKeyFile)
if len(privateKey) != ed25519.PrivateKeySize {
log.Fatal("Invalid private key.")
}
rc.PrivateKey = dnscrypt.HexEncodeKey(privateKey)
// make PublicKey
publicKey := privateKey.Public().(ed25519.PublicKey)
rc.PublicKey = dnscrypt.HexEncodeKey(publicKey)
// make ResolverSk
var resolverSecret ed25519.PrivateKey
resolverSecret = getFileContent(args.ResolverSkFile)
if len(resolverSecret) != 32 {
log.Fatal("Invalid resolver secret key.")
}
rc.ResolverSk = dnscrypt.HexEncodeKey(resolverSecret)
// make ResolverPk
resolverPublic := getResolverPk(resolverSecret)
rc.ResolverPk = dnscrypt.HexEncodeKey(resolverPublic)
if err := validateRc(rc, publicKey); err != nil {
log.Fatalf("Failed to validate resolver config, err: %s", err.Error())
}
out, err := yaml.Marshal(rc)
if err != nil {
log.Fatalf("Failed to marshall output config, err: %s", err.Error())
}
err = ioutil.WriteFile(args.Out, out, 0600)
if err != nil {
log.Fatalf("Failed to write file, err: %s", err.Error())
}
}
// validateRc - verifies that the certificate is correctly
// created and validated for this resolver config. if rc valid returns nil.
func validateRc(rc dnscrypt.ResolverConfig, publicKey ed25519.PublicKey) error {
cert, err := rc.CreateCert()
if err != nil {
return fmt.Errorf("failed to validate cert, err: %s", err.Error())
}
if cert == nil {
return fmt.Errorf("created cert is empty")
}
if !cert.VerifyDate() {
return fmt.Errorf("cert date is not valid")
}
if !cert.VerifySignature(publicKey) {
return fmt.Errorf("cert signed incorrectly")
}
return nil
}
// getResolverPk - calculates public key from private key
func getResolverPk(private ed25519.PrivateKey) ed25519.PublicKey {
resolverSk := [32]byte{}
resolverPk := [32]byte{}
copy(resolverSk[:], private)
curve25519.ScalarBaseMult(&resolverPk, &resolverSk)
return resolverPk[:]
}
func getFileContent(fname string) []byte {
bytes, err := ioutil.ReadFile(fname)
if err != nil {
log.Fatalf("Fail read key file %s, err: %s", fname, err.Error())
}
return bytes
}

View file

@ -10,10 +10,10 @@ import (
// GenerateArgs - "generate" command arguments
type GenerateArgs struct {
ProviderName string `short:"p" long:"provider-name" description:"DNSCrypt provider name" required:"true"`
ProviderName string `short:"p" long:"provider-name" description:"DNSCrypt provider name. Param is required." required:"true"`
Out string `short:"o" long:"out" description:"Path to the resulting config file. Param is required." required:"true"`
PrivateKey string `short:"k" long:"private-key" description:"Private key (hex-encoded)"`
CertificateTTL int `short:"t" long:"ttl" description:"Certificate time-to-live (seconds)"`
Out string `short:"o" long:"out" description:"Path to the resulting config file" required:"true"`
}
// generate - generates DNSCrypt server configuration
@ -40,7 +40,7 @@ func generate(args GenerateArgs) {
}
// nolint
err = ioutil.WriteFile(args.Out, b, 0644)
err = ioutil.WriteFile(args.Out, b, 0600)
if err != nil {
log.Fatalf("failed to save %s: %v", args.Out, err)
}

View file

@ -15,18 +15,18 @@ import (
// LookupStampArgs - "lookup-stamp" command arguments
type LookupStampArgs struct {
Network string `short:"n" long:"network" description:"network type (tcp/udp)" default:"udp"`
Stamp string `short:"s" long:"stamp" description:"DNSCrypt resolver stamp" required:"true"`
Domain string `short:"d" long:"domain" description:"Domain to resolve" required:"true"`
Stamp string `short:"s" long:"stamp" description:"DNSCrypt resolver stamp. Param is required." required:"true"`
Domain string `short:"d" long:"domain" description:"Domain to resolve. Param is required." required:"true"`
Type string `short:"t" long:"type" description:"DNS query type" default:"A"`
}
// LookupArgs - "lookup" command arguments
type LookupArgs struct {
Network string `short:"n" long:"network" description:"network type (tcp/udp)" default:"udp"`
ProviderName string `short:"p" long:"provider-name" description:"DNSCrypt resolver provider name" required:"true"`
PublicKey string `short:"k" long:"public-key" description:"DNSCrypt resolver public key" required:"true"`
ServerAddr string `short:"a" long:"addr" description:"Resolver address (IP[:port]). By default, the port is 443" required:"true"`
Domain string `short:"d" long:"domain" description:"Domain to resolve" required:"true"`
ProviderName string `short:"p" long:"provider-name" description:"DNSCrypt resolver provider name. Param is required." required:"true"`
PublicKey string `short:"k" long:"public-key" description:"DNSCrypt resolver public key. Param is required." required:"true"`
ServerAddr string `short:"a" long:"addr" description:"Resolver address (IP[:port]). By default, the port is 443. Param is required." required:"true"`
Domain string `short:"d" long:"domain" description:"Domain to resolve. Param is required." required:"true"`
Type string `short:"t" long:"type" description:"DNS query type" default:"A"`
}

View file

@ -10,11 +10,12 @@ import (
// Options - command-line options
type Options struct {
Generate GenerateArgs `command:"generate" description:"Generates DNSCrypt server configuration"`
LookupStamp LookupStampArgs `command:"lookup-stamp" description:"Performs a DNSCrypt lookup for the specified domain using an sdns:// stamp"`
Lookup LookupArgs `command:"lookup" description:"Performs a DNSCrypt lookup for the specified domain"`
Server ServerArgs `command:"server" description:"Runs a DNSCrypt resolver"`
Version struct {
Generate GenerateArgs `command:"generate" description:"Generates DNSCrypt server configuration"`
LookupStamp LookupStampArgs `command:"lookup-stamp" description:"Performs a DNSCrypt lookup for the specified domain using an sdns:// stamp"`
Lookup LookupArgs `command:"lookup" description:"Performs a DNSCrypt lookup for the specified domain"`
Server ServerArgs `command:"server" description:"Runs a DNSCrypt resolver"`
ConvertWrapper ConvertWrapperArgs `command:"convert-dnscrypt-wrapper" description:"Converting keys generated with dnscrypt-wrapper to yaml config"`
Version struct {
} `command:"version" description:"Prints version"`
}
@ -45,6 +46,8 @@ func main() {
lookup(opts.Lookup)
case "server":
server(opts.Server)
case "convert-dnscrypt-wrapper":
convertWrapper(opts.ConvertWrapper)
default:
log.Fatalf("unknown command %s", parser.Active.Name)
}

View file

@ -15,7 +15,7 @@ import (
// ServerArgs - "server" command arguments
type ServerArgs struct {
Config string `short:"c" long:"config" description:"Path to the DNSCrypt configuration file" required:"true"`
Config string `short:"c" long:"config" description:"Path to the DNSCrypt configuration file. Param is required." required:"true"`
Forward string `short:"f" long:"forward" description:"Forwards DNS queries to the specified address" default:"94.140.14.140:53"`
ListenAddrs []string `short:"l" long:"listen" description:"Listening addresses" default:"0.0.0.0"`
ListenPorts []int `short:"p" long:"port" description:"Listening ports" default:"443"`

View file

@ -31,7 +31,7 @@ type ResolverConfig struct {
// If not set, we'll generate a new random ResolverSk and ResolverPk.
ResolverSk string `yaml:"resolver_secret"`
// ResolverSk - hex-encoded short-term public key corresponding to ResolverSk.
// ResolverPk - hex-encoded short-term public key corresponding to ResolverSk.
// This key is used to encrypt/decrypt DNS queries.
ResolverPk string `yaml:"resolver_public"`