mirror of
https://github.com/SamTherapy/dnscrypt.git
synced 2024-12-22 00:50:42 +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:
parent
053da1d64f
commit
d0ae1d198d
7 changed files with 179 additions and 34 deletions
70
README.md
70
README.md
|
@ -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
|
||||
|
|
108
cmd/convert_dnscrypt_wrapper.go
Normal file
108
cmd/convert_dnscrypt_wrapper.go
Normal 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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
||||
|
|
13
cmd/main.go
13
cmd/main.go
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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"`
|
||||
|
||||
|
|
Loading…
Reference in a new issue