Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
accce088e6 | ||
![]() |
42beefd223 | ||
![]() |
12b71780a1 | ||
![]() |
b444857549 | ||
![]() |
2004e84df8 | ||
![]() |
7441572846 | ||
![]() |
93dd1b4e8d | ||
![]() |
d467b652ec | ||
![]() |
19c29cfdc6 |
24
README.md
24
README.md
@@ -10,7 +10,7 @@ If you have go installed you can download, build, and install this tool with `go
|
||||
|
||||
```
|
||||
go get i2pgit.org/idk/reseed-tools
|
||||
i2p-tools -h
|
||||
reseed-tools -h
|
||||
```
|
||||
|
||||
## Usage
|
||||
@@ -76,13 +76,13 @@ work for you. In that case, just copy-and-paste:
|
||||
### Locally behind a webserver (reverse proxy setup), preferred:
|
||||
|
||||
```
|
||||
i2p-tools reseed --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --port=8443 --ip=127.0.0.1 --trustProxy
|
||||
reseed-tools reseed --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --port=8443 --ip=127.0.0.1 --trustProxy
|
||||
```
|
||||
|
||||
### Without a webserver, standalone with TLS support
|
||||
|
||||
```
|
||||
i2p-tools reseed --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --tlsHost=your-domain.tld
|
||||
reseed-tools reseed --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --tlsHost=your-domain.tld
|
||||
```
|
||||
|
||||
If this is your first time running a reseed server (ie. you don't have any existing keys),
|
||||
@@ -103,25 +103,25 @@ Requires ```go mod``` and at least go 1.13. To build the idk/reseed-tools
|
||||
fork, from anywhere:
|
||||
|
||||
git clone https://i2pgit.org/idk/reseed-tools
|
||||
cd i2p-tools-1
|
||||
cd reseed-tools
|
||||
make build
|
||||
|
||||
### Without a webserver, standalone, self-supervising(Automatic restarts)
|
||||
|
||||
```
|
||||
./i2p-tools-1 reseed --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --littleboss=start
|
||||
./reseed-tools reseed --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --littleboss=start
|
||||
```
|
||||
|
||||
### Without a webserver, standalone, automatic OnionV3 with TLS support
|
||||
|
||||
```
|
||||
./i2p-tools-1 reseed --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --onion --i2p --p2p
|
||||
./reseed-tools reseed --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --onion --i2p --p2p
|
||||
```
|
||||
|
||||
### Without a webserver, standalone, serve P2P with LibP2P
|
||||
|
||||
```
|
||||
./i2p-tools-1 reseed --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --p2p
|
||||
./reseed-tools reseed --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --p2p
|
||||
```
|
||||
|
||||
### Without a webserver, standalone, upload a single signed .su3 to github
|
||||
@@ -129,29 +129,29 @@ fork, from anywhere:
|
||||
* This one isn't working yet, I'll get to it eventually, I've got a cooler idea now.
|
||||
|
||||
```
|
||||
./i2p-tools-1 reseed --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --github --ghrepo=i2p-tools-1 --ghuser=eyedeekay
|
||||
./reseed-tools reseed --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --github --ghrepo=reseed-tools --ghuser=eyedeekay
|
||||
```
|
||||
|
||||
### Without a webserver, standalone, in-network reseed
|
||||
|
||||
```
|
||||
./i2p-tools-1 reseed --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --i2p
|
||||
./reseed-tools reseed --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --i2p
|
||||
```
|
||||
|
||||
### Without a webserver, standalone, Regular TLS, OnionV3 with TLS
|
||||
|
||||
```
|
||||
./i2p-tools-1 reseed --tlsHost=your-domain.tld --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --onion
|
||||
./reseed-tools reseed --tlsHost=your-domain.tld --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --onion
|
||||
```
|
||||
|
||||
### Without a webserver, standalone, Regular TLS, OnionV3 with TLS, and LibP2P
|
||||
|
||||
```
|
||||
./i2p-tools-1 reseed --tlsHost=your-domain.tld --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --onion --p2p
|
||||
./reseed-tools reseed --tlsHost=your-domain.tld --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --onion --p2p
|
||||
```
|
||||
|
||||
### Without a webserver, standalone, Regular TLS, OnionV3 with TLS, I2P In-Network reseed, and LibP2P, self-supervising
|
||||
|
||||
```
|
||||
./i2p-tools-1 reseed --tlsHost=your-domain.tld --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --onion --p2p --littleboss=start
|
||||
./reseed-tools reseed --tlsHost=your-domain.tld --signer=you@mail.i2p --netdb=/home/i2p/.i2p/netDb --onion --p2p --littleboss=start
|
||||
```
|
||||
|
@@ -133,6 +133,15 @@ func NewReseedCommand() cli.Command {
|
||||
Value: "start",
|
||||
Usage: "Self-Supervise this application",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "acme",
|
||||
Usage: "Automatically generate a TLS certificate with the ACME protocol, defaults to Let's Encrypt",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "acmeserver",
|
||||
Value: "https://acme-staging-v02.api.letsencrypt.org/directory",
|
||||
Usage: "Use this server to issue a certificate with the ACME protocol",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -208,13 +217,53 @@ func reseedAction(c *cli.Context) {
|
||||
var i2pTlsCert, i2pTlsKey string
|
||||
var i2pkey i2pkeys.I2PKeys
|
||||
|
||||
if tlsHost != "" {
|
||||
onionTlsHost = tlsHost
|
||||
i2pTlsHost = tlsHost
|
||||
tlsKey = c.String("tlsKey")
|
||||
// if no key is specified, default to the host.pem in the current dir
|
||||
if tlsKey == "" {
|
||||
tlsKey = tlsHost + ".pem"
|
||||
onionTlsKey = tlsHost + ".pem"
|
||||
i2pTlsKey = tlsHost + ".pem"
|
||||
}
|
||||
|
||||
tlsCert = c.String("tlsCert")
|
||||
// if no certificate is specified, default to the host.crt in the current dir
|
||||
if tlsCert == "" {
|
||||
tlsCert = tlsHost + ".crt"
|
||||
onionTlsCert = tlsHost + ".crt"
|
||||
i2pTlsCert = tlsHost + ".crt"
|
||||
}
|
||||
|
||||
// prompt to create tls keys if they don't exist?
|
||||
auto := c.Bool("yes")
|
||||
// use ACME?
|
||||
acme := c.Bool("acme")
|
||||
if acme {
|
||||
acmeserver := c.String("acmeserver")
|
||||
err := checkUseAcmeCert(tlsHost, signerID, acmeserver, &tlsCert, &tlsKey, auto)
|
||||
if nil != err {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
} else {
|
||||
err := checkOrNewTLSCert(tlsHost, &tlsCert, &tlsKey, auto)
|
||||
if nil != err {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if c.Bool("i2p") {
|
||||
var err error
|
||||
i2pkey, err = LoadKeys("reseed.i2pkeys", c)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
i2pTlsHost = i2pkey.Addr().Base32()
|
||||
if i2pTlsHost == "" {
|
||||
i2pTlsHost = i2pkey.Addr().Base32()
|
||||
}
|
||||
if i2pTlsHost != "" {
|
||||
// if no key is specified, default to the host.pem in the current dir
|
||||
if i2pTlsKey == "" {
|
||||
@@ -250,7 +299,9 @@ func reseedAction(c *cli.Context) {
|
||||
}
|
||||
ok = []byte(key.PrivateKey())
|
||||
}
|
||||
onionTlsHost = torutil.OnionServiceIDFromPrivateKey(ed25519.PrivateKey(ok)) + ".onion"
|
||||
if onionTlsHost == "" {
|
||||
onionTlsHost = torutil.OnionServiceIDFromPrivateKey(ed25519.PrivateKey(ok)) + ".onion"
|
||||
}
|
||||
err = ioutil.WriteFile(c.String("onionKey"), ok, 0644)
|
||||
if err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
@@ -275,27 +326,6 @@ func reseedAction(c *cli.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
if tlsHost != "" {
|
||||
tlsKey = c.String("tlsKey")
|
||||
// if no key is specified, default to the host.pem in the current dir
|
||||
if tlsKey == "" {
|
||||
tlsKey = tlsHost + ".pem"
|
||||
}
|
||||
|
||||
tlsCert = c.String("tlsCert")
|
||||
// if no certificate is specified, default to the host.crt in the current dir
|
||||
if tlsCert == "" {
|
||||
tlsCert = tlsHost + ".crt"
|
||||
}
|
||||
|
||||
// prompt to create tls keys if they don't exist?
|
||||
auto := c.Bool("yes")
|
||||
err := checkOrNewTLSCert(tlsHost, &tlsCert, &tlsKey, auto)
|
||||
if nil != err {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
reloadIntvl, err := time.ParseDuration(c.String("interval"))
|
||||
if nil != err {
|
||||
fmt.Printf("'%s' is not a valid time interval.\n", reloadIntvl)
|
||||
|
186
cmd/utils.go
186
cmd/utils.go
@@ -2,10 +2,12 @@ package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
@@ -18,6 +20,13 @@ import (
|
||||
|
||||
"i2pgit.org/idk/reseed-tools/reseed"
|
||||
"i2pgit.org/idk/reseed-tools/su3"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/challenge/http01"
|
||||
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
)
|
||||
|
||||
func loadPrivateKey(path string) (*rsa.PrivateKey, error) {
|
||||
@@ -35,6 +44,24 @@ func loadPrivateKey(path string) (*rsa.PrivateKey, error) {
|
||||
return privKey, nil
|
||||
}
|
||||
|
||||
// Taken directly from the lego example, since we need very minimal support
|
||||
// https://go-acme.github.io/lego/usage/library/
|
||||
type MyUser struct {
|
||||
Email string
|
||||
Registration *registration.Resource
|
||||
key crypto.PrivateKey
|
||||
}
|
||||
|
||||
func (u *MyUser) GetEmail() string {
|
||||
return u.Email
|
||||
}
|
||||
func (u MyUser) GetRegistration() *registration.Resource {
|
||||
return u.Registration
|
||||
}
|
||||
func (u *MyUser) GetPrivateKey() crypto.PrivateKey {
|
||||
return u.key
|
||||
}
|
||||
|
||||
func signerFile(signerID string) string {
|
||||
return strings.Replace(signerID, "@", "_at_", 1)
|
||||
}
|
||||
@@ -60,6 +87,165 @@ func getOrNewSigningCert(signerKey *string, signerID string, auto bool) (*rsa.Pr
|
||||
return loadPrivateKey(*signerKey)
|
||||
}
|
||||
|
||||
func checkUseAcmeCert(tlsHost, signer, cadirurl string, tlsCert, tlsKey *string, auto bool) error {
|
||||
_, certErr := os.Stat(*tlsCert)
|
||||
_, keyErr := os.Stat(*tlsKey)
|
||||
if certErr != nil || keyErr != nil {
|
||||
if certErr != nil {
|
||||
fmt.Printf("Unable to read TLS certificate '%s'\n", *tlsCert)
|
||||
}
|
||||
if keyErr != nil {
|
||||
fmt.Printf("Unable to read TLS key '%s'\n", *tlsKey)
|
||||
}
|
||||
|
||||
if !auto {
|
||||
fmt.Printf("Would you like to generate a new certificate with Let's Encrypt or a custom ACME server? '%s'? (y or n): ", tlsHost)
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
input, _ := reader.ReadString('\n')
|
||||
if []byte(input)[0] != 'y' {
|
||||
fmt.Println("Continuing without TLS")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TLSConfig := &tls.Config{}
|
||||
TLSConfig.NextProtos = []string{"http/1.1"}
|
||||
TLSConfig.Certificates = make([]tls.Certificate, 1)
|
||||
var err error
|
||||
TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(*tlsCert, *tlsKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if time.Now().Sub(TLSConfig.Certificates[0].Leaf.NotAfter) < (time.Hour * 48) {
|
||||
ecder, err := ioutil.ReadFile(tlsHost + signer + ".acme.key")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
privateKey, err := x509.ParseECPrivateKey(ecder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user := MyUser{
|
||||
Email: signer,
|
||||
key: privateKey,
|
||||
}
|
||||
config := lego.NewConfig(&user)
|
||||
config.CADirURL = cadirurl
|
||||
config.Certificate.KeyType = certcrypto.RSA2048
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
renewAcmeIssuedCert(client, user, tlsHost, tlsCert, tlsKey)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ecder, err := x509.MarshalECPrivateKey(privateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filename := tlsHost + signer + ".acme.key"
|
||||
keypem, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer keypem.Close()
|
||||
err = pem.Encode(keypem, &pem.Block{Type: "EC PRIVATE KEY", Bytes: ecder})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user := MyUser{
|
||||
Email: signer,
|
||||
key: privateKey,
|
||||
}
|
||||
config := lego.NewConfig(&user)
|
||||
config.CADirURL = cadirurl
|
||||
config.Certificate.KeyType = certcrypto.RSA2048
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return newAcmeIssuedCert(client, user, tlsHost, tlsCert, tlsKey)
|
||||
}
|
||||
|
||||
func renewAcmeIssuedCert(client *lego.Client, user MyUser, tlsHost string, tlsCert, tlsKey *string) error {
|
||||
var err error
|
||||
err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "8000"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", "8443"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// New users will need to register
|
||||
if user.Registration, err = client.Registration.QueryRegistration(); err != nil {
|
||||
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user.Registration = reg
|
||||
}
|
||||
resource, err := client.Certificate.Get(tlsHost, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
certificates, err := client.Certificate.Renew(*resource, true, false, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ioutil.WriteFile(tlsHost+".pem", certificates.PrivateKey, 0600)
|
||||
ioutil.WriteFile(tlsHost+".crt", certificates.Certificate, 0600)
|
||||
// ioutil.WriteFile(tlsHost+".crl", certificates.PrivateKey, 0600)
|
||||
*tlsCert = tlsHost + ".crt"
|
||||
*tlsKey = tlsHost + ".pem"
|
||||
return nil
|
||||
}
|
||||
|
||||
func newAcmeIssuedCert(client *lego.Client, user MyUser, tlsHost string, tlsCert, tlsKey *string) error {
|
||||
var err error
|
||||
err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "8000"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", "8443"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// New users will need to register
|
||||
if user.Registration, err = client.Registration.QueryRegistration(); err != nil {
|
||||
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user.Registration = reg
|
||||
}
|
||||
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: []string{tlsHost},
|
||||
Bundle: true,
|
||||
}
|
||||
certificates, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ioutil.WriteFile(tlsHost+".pem", certificates.PrivateKey, 0600)
|
||||
ioutil.WriteFile(tlsHost+".crt", certificates.Certificate, 0600)
|
||||
// ioutil.WriteFile(tlsHost+".crl", certificates.PrivateKey, 0600)
|
||||
*tlsCert = tlsHost + ".crt"
|
||||
*tlsKey = tlsHost + ".pem"
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkOrNewTLSCert(tlsHost string, tlsCert, tlsKey *string, auto bool) error {
|
||||
_, certErr := os.Stat(*tlsCert)
|
||||
_, keyErr := os.Stat(*tlsKey)
|
||||
|
24
go.mod
24
go.mod
@@ -3,39 +3,17 @@ module i2pgit.org/idk/reseed-tools
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/btcsuite/btcd v0.21.0-beta // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/cretz/bine v0.1.0
|
||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
|
||||
github.com/eyedeekay/ramp v0.0.0-20190429201811-305b382042ab // indirect
|
||||
github.com/eyedeekay/sam3 v0.32.32
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/gopacket v1.1.19 // indirect
|
||||
github.com/google/uuid v1.2.0 // indirect
|
||||
github.com/go-acme/lego/v4 v4.3.1
|
||||
github.com/gorilla/handlers v1.5.1
|
||||
github.com/jackpal/gateway v1.0.6 // indirect
|
||||
github.com/justinas/alice v1.2.0
|
||||
github.com/koron/go-ssdp v0.0.2 // indirect
|
||||
github.com/libp2p/go-libp2p v0.13.0
|
||||
github.com/libp2p/go-libp2p-core v0.8.0
|
||||
github.com/libp2p/go-libp2p-gostream v0.3.1
|
||||
github.com/libp2p/go-libp2p-http v0.2.0
|
||||
github.com/libp2p/go-libp2p-noise v0.1.2 // indirect
|
||||
github.com/libp2p/go-netroute v0.1.4 // indirect
|
||||
github.com/libp2p/go-sockaddr v0.1.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sridharv/gojava v0.0.0-20180117154747-891bb0316909 // indirect
|
||||
github.com/sridharv/gomobile-java v0.0.0-20160328180427-34d2814361d9 // indirect
|
||||
github.com/throttled/throttled v2.2.4+incompatible
|
||||
github.com/throttled/throttled/v2 v2.7.1
|
||||
github.com/urfave/cli v1.22.5
|
||||
gitlab.com/golang-commonmark/linkify v0.0.0-20200225224916-64bca66f6ad3 // indirect
|
||||
gitlab.com/golang-commonmark/markdown v0.0.0-20191127184510-91b5b3c99c19
|
||||
go.opencensus.io v0.22.6 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.16.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
|
||||
golang.org/x/text v0.3.5
|
||||
)
|
||||
|
Reference in New Issue
Block a user