refactor
This commit is contained in:
99
reseed/cert.go
Normal file
99
reseed/cert.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package reseed
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GenerateCert(host string, validFrom string, validFor time.Duration, isCA bool, rsaBits int) {
|
||||
if len(host) == 0 {
|
||||
log.Fatalf("Missing required -host parameter")
|
||||
}
|
||||
|
||||
priv, err := rsa.GenerateKey(rand.Reader, rsaBits)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to generate private key: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
var notBefore time.Time
|
||||
if len(validFrom) == 0 {
|
||||
notBefore = time.Now()
|
||||
} else {
|
||||
notBefore, err = time.Parse("Jan 2 15:04:05 2006", validFrom)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to parse creation date: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
notAfter := notBefore.Add(validFor)
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to generate serial number: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"I2P"},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
hosts := strings.Split(host, ",")
|
||||
for _, h := range hosts {
|
||||
if ip := net.ParseIP(h); ip != nil {
|
||||
template.IPAddresses = append(template.IPAddresses, ip)
|
||||
} else {
|
||||
template.DNSNames = append(template.DNSNames, h)
|
||||
}
|
||||
}
|
||||
|
||||
if isCA {
|
||||
template.IsCA = true
|
||||
template.KeyUsage |= x509.KeyUsageCertSign
|
||||
}
|
||||
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create certificate: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
certOut, err := os.Create("cert.pem")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to open cert.pem for writing: %s", err)
|
||||
return
|
||||
}
|
||||
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
certOut.Close()
|
||||
log.Print("written cert.pem\n")
|
||||
|
||||
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.Print("failed to open key.pem for writing:", err)
|
||||
return
|
||||
}
|
||||
pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
|
||||
keyOut.Close()
|
||||
log.Print("written key.pem\n")
|
||||
}
|
||||
224
reseed/legacy.go
Normal file
224
reseed/legacy.go
Normal file
@@ -0,0 +1,224 @@
|
||||
package reseed
|
||||
|
||||
// read in all files from netdb dir into a slice of routerinfos
|
||||
|
||||
// for every unique requesting IP
|
||||
// look up that IP in the db
|
||||
// - if it exists, check the creation time
|
||||
// - if the creation time is within the threshold, serve up the routerinfos
|
||||
// - if the creation time is outside the threshold, or if it does not exist generate a new slice of routerinfos from the current master set
|
||||
|
||||
// at some regular interval, update the master slice with fresh netdb routerinfos
|
||||
|
||||
// can serve up html/ul of routerinfos
|
||||
// can serve up su3 signed file
|
||||
// https://geti2p.net/en/docs/spec/updates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/PuerkitoBio/throttled"
|
||||
"github.com/PuerkitoBio/throttled/store"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
LIST_TEMPLATE = `<html><head><title>NetDB</title></head><body><ul>{{ range $index, $_ := . }}<li><a href="{{ $index }}">{{ $index }}</a></li>{{ end }}</ul></body></html>`
|
||||
)
|
||||
|
||||
func proxiedHandler(h http.Handler) http.Handler {
|
||||
return remoteAddrFixup{h}
|
||||
}
|
||||
|
||||
type remoteAddrFixup struct {
|
||||
h http.Handler
|
||||
}
|
||||
|
||||
func (h remoteAddrFixup) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if prior, ok := r.Header["X-Forwarded-For"]; ok {
|
||||
r.RemoteAddr = prior[0]
|
||||
}
|
||||
h.h.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
NetDBDir string
|
||||
RefreshInterval time.Duration
|
||||
Proxy bool
|
||||
Verbose bool
|
||||
RateLimit int
|
||||
|
||||
Addr string
|
||||
Port string
|
||||
Cert string
|
||||
Key string
|
||||
}
|
||||
|
||||
func Run(config *Config) {
|
||||
legacyReseeder := NewLegacyReseeder(config.NetDBDir)
|
||||
legacyReseeder.Start(config.RefreshInterval)
|
||||
|
||||
su3Reseeder := NewSu3Reseeder(config.NetDBDir)
|
||||
su3Reseeder.Start()
|
||||
|
||||
r := mux.NewRouter()
|
||||
s := r.PathPrefix("/netdb").Subrouter()
|
||||
|
||||
s.HandleFunc("/", legacyReseeder.ListHandler)
|
||||
s.HandleFunc(`/routerInfo-{hash:[A-Za-z0-9-=~]+}.dat`, legacyReseeder.RouterInfoHandler)
|
||||
s.HandleFunc("/i2pseeds.su3", su3Reseeder.Su3Handler)
|
||||
|
||||
th := throttled.RateLimit(throttled.PerMin(config.RateLimit), &throttled.VaryBy{RemoteAddr: true}, store.NewMemStore(1000))
|
||||
muxWithMiddlewares := th.Throttle(r)
|
||||
|
||||
if config.Proxy {
|
||||
muxWithMiddlewares = proxiedHandler(muxWithMiddlewares)
|
||||
}
|
||||
|
||||
if config.Verbose {
|
||||
muxWithMiddlewares = handlers.CombinedLoggingHandler(os.Stdout, muxWithMiddlewares)
|
||||
}
|
||||
|
||||
listenAddress := fmt.Sprintf("%s:%s", config.Addr, config.Port)
|
||||
|
||||
if config.Cert != "" && config.Key != "" {
|
||||
log.Println("Starting https reseed server on " + listenAddress)
|
||||
err := http.ListenAndServeTLS(listenAddress, config.Cert, config.Key, muxWithMiddlewares)
|
||||
if nil != err {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
} else {
|
||||
log.Println("Starting http reseed server on " + listenAddress)
|
||||
err := http.ListenAndServe(listenAddress, muxWithMiddlewares)
|
||||
if nil != err {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewLegacyReseeder(netdbDir string) *LegacyReseeder {
|
||||
return &LegacyReseeder{netdbDir: netdbDir, nextMap: make(chan map[string][]byte)}
|
||||
}
|
||||
|
||||
func NewSu3Reseeder(netdbDir string) *Su3Reseeder {
|
||||
return &Su3Reseeder{netdbDir: netdbDir, nextMap: make(chan []string)}
|
||||
}
|
||||
|
||||
type Su3Reseeder struct {
|
||||
nextMap chan []string
|
||||
netdbDir string
|
||||
}
|
||||
|
||||
func (r *Su3Reseeder) Start() {
|
||||
}
|
||||
|
||||
func (rs *Su3Reseeder) Su3Handler(w http.ResponseWriter, r *http.Request) {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
type LegacyReseeder struct {
|
||||
nextMap chan map[string][]byte
|
||||
netdbDir string
|
||||
listTemplate *template.Template
|
||||
}
|
||||
|
||||
func (r *LegacyReseeder) Start(refreshInterval time.Duration) {
|
||||
go func() {
|
||||
var m map[string][]byte
|
||||
for {
|
||||
select {
|
||||
case m = <-r.nextMap:
|
||||
case r.nextMap <- m:
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
log.Println("Updating routerInfos")
|
||||
r.Refresh(r.netdbDir)
|
||||
time.Sleep(refreshInterval)
|
||||
}
|
||||
}()
|
||||
|
||||
// parse the template for routerInfo lists
|
||||
var err error
|
||||
r.listTemplate, err = template.New("ri").Parse(LIST_TEMPLATE)
|
||||
if err != nil {
|
||||
log.Fatalln("error parsing routerInfo list template", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r *LegacyReseeder) Refresh(netdbDir string) {
|
||||
m := make(map[string][]byte)
|
||||
|
||||
src, err := ioutil.ReadDir(netdbDir)
|
||||
if nil != err {
|
||||
log.Fatalln("error reading netdb dir", err)
|
||||
return
|
||||
}
|
||||
|
||||
// randomize the file order
|
||||
files := make([]os.FileInfo, len(src))
|
||||
perm := rand.Perm(len(src))
|
||||
for i, v := range perm {
|
||||
files[v] = src[i]
|
||||
}
|
||||
|
||||
added := 0
|
||||
for _, file := range files {
|
||||
if match, _ := regexp.MatchString("^routerInfo-[A-Za-z0-9-=~]+.dat$", file.Name()); match {
|
||||
fi, err := os.Open(netdbDir + "/" + file.Name())
|
||||
if err != nil {
|
||||
log.Println("Error reading routerInfo file", err)
|
||||
continue
|
||||
}
|
||||
fileData, err := ioutil.ReadAll(fi)
|
||||
if nil != err {
|
||||
log.Println("Error reading routerInfo file", err)
|
||||
continue
|
||||
}
|
||||
fi.Close()
|
||||
|
||||
m[file.Name()] = fileData
|
||||
added++
|
||||
}
|
||||
if added >= 50 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
r.nextMap <- m
|
||||
}
|
||||
|
||||
func (lr *LegacyReseeder) ListHandler(w http.ResponseWriter, r *http.Request) {
|
||||
err := lr.listTemplate.Execute(w, <-lr.nextMap)
|
||||
if err != nil {
|
||||
log.Fatalln("error rending list template", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (rs *LegacyReseeder) RouterInfoHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
fileName := "routerInfo-" + vars["hash"] + ".dat"
|
||||
|
||||
m := <-rs.nextMap
|
||||
if b, ok := m[fileName]; ok {
|
||||
io.Copy(w, bytes.NewReader(b))
|
||||
return
|
||||
}
|
||||
|
||||
http.NotFound(w, r)
|
||||
log.Println("error sending file", fileName)
|
||||
}
|
||||
5
reseed/server.go
Normal file
5
reseed/server.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package reseed
|
||||
|
||||
const (
|
||||
LIST_TEMPLATE = `<html><head><title>NetDB</title></head><body><ul>{{ range $index, $_ := . }}<li><a href="{{ $index }}">{{ $index }}</a></li>{{ end }}</ul></body></html>`
|
||||
)
|
||||
Reference in New Issue
Block a user