25 Commits

Author SHA1 Message Date
eyedeekay
00aafc2ede fix the broken tests 2024-11-13 14:55:42 -05:00
eyedeekay
8920b7c847 update version 2024-11-13 14:53:54 -05:00
eyedeekay
2791849c67 update modules 2024-11-13 14:52:44 -05:00
eyedeekay
0d9a9ee10a Fix up import paths 2024-11-09 11:54:29 -05:00
eyedeekay
867026628f setup auto-assign workflow 2024-11-08 15:01:15 -05:00
eyedeekay
56eed1e88f Merge branch 'main' of github.com:eyedeekay/onramp 2024-11-08 12:55:30 -05:00
eyedeekay
c4e23394dc change module path 2024-11-08 12:54:30 -05:00
idk
f9fc572938 Merge pull request #1 from hkh4n/logging
Logging
2024-11-05 00:03:47 +00:00
Haris Khan
e184eda3c1 replaced deprecated function 2024-11-04 15:20:23 -05:00
Haris Khan
a0a35dd81c added logging to tls.go 2024-11-04 15:17:48 -05:00
Haris Khan
7ef5793e5a added logging to proxy.go 2024-11-04 15:11:49 -05:00
Haris Khan
c2b8660d5e added logging to onion.go 2024-11-04 15:08:35 -05:00
Haris Khan
680d5c000b added logging to garlic.go 2024-11-04 14:52:43 -05:00
Haris Khan
c2b56d35e2 updated README.md to reflect new logging paradigm 2024-11-04 14:44:07 -05:00
Haris Khan
2a53e8347c added logging to common.go 2024-11-04 11:05:47 -05:00
Haris Khan
dcb997a327 added logging paradigm to log.go 2024-11-04 11:05:39 -05:00
eyedeekay
d6a5a60d6f Fix broken test, fix onion TLS listener which broke 2024-09-17 20:10:29 -04:00
eyedeekay
c60c135814 Update sam3 and i2pkeys to 0.33.8 2024-09-17 19:41:29 -04:00
eyedeekay
8f08410320 add credit for contributions to release info 2024-09-17 19:35:32 -04:00
eyedeekay
cdad2747a7 bump version to 0.33.7 2024-01-09 14:48:35 -05:00
eyedeekay
394c9f9fb8 Update sam3 version to v0.33.7 2024-01-09 14:48:08 -05:00
eyedeekay
f32a695cff stub out libp2p interface 2023-08-06 22:36:36 -04:00
eyedeekay
41dcb29429 temporarily remove WIP libp2p support 2023-07-21 15:24:54 -04:00
eyedeekay
0e2d9a60a4 prepare for official releases 2023-07-21 15:15:29 -04:00
idk
f654f2635b start a libp2p transport 2023-06-25 08:25:19 -04:00
14 changed files with 680 additions and 119 deletions

20
.github/workflows/auto-assign.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: Auto Assign
on:
issues:
types: [opened]
pull_request:
types: [opened]
jobs:
run:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- name: 'Auto-assign issue'
uses: pozil/auto-assign-issue@v1
with:
repo-token:${{ secrets.GITHUB_TOKEN }}
assignees: eyedeekay
numOfAssignee: 1

28
Makefile Normal file
View File

@@ -0,0 +1,28 @@
USER_GH=eyedeekay
VERSION=0.33.9
CREDIT='contributors to this release: @hkh4n, @eyedeekay'
packagename=onramp
echo: fmt
@echo "type make version to do release $(VERSION)"
version:
github-release release -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION) -d "version $(VERSION) $(CREDIT)"
del:
github-release delete -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION)
tar:
tar --exclude .git \
--exclude .go \
--exclude bin \
--exclude examples \
-cJvf ../$(packagename)_$(VERSION).orig.tar.xz .
link:
rm -f ../goSam
ln -sf . ../goSam
fmt:
find . -name '*.go' -exec gofmt -w -s {} \;

View File

@@ -77,3 +77,31 @@ func main() {
defer listener.Close()
}
```
## Verbosity ##
Logging can be enabled and configured using the DEBUG_I2P environment variable. By default, logging is disabled.
There are three available log levels:
- Debug
```shell
export DEBUG_I2P=debug
```
- Warn
```shell
export DEBUG_I2P=warn
```
- Error
```shell
export DEBUG_I2P=error
```
If DEBUG_I2P is set to an unrecognized variable, it will fall back to "debug".
## Contributing
See CONTRIBUTING.md for more information.
## License
This project is licensed under the MIT license, see LICENSE for more information.

View File

@@ -4,6 +4,7 @@
package onramp
import (
"github.com/sirupsen/logrus"
"net"
"net/url"
"os"
@@ -17,16 +18,23 @@ import (
func GetJoinedWD(dir string) (string, error) {
wd, err := os.Getwd()
if err != nil {
log.WithError(err).Error("Failed to get working directory")
return "", err
}
jwd := filepath.Join(wd, dir)
ajwd, err := filepath.Abs(jwd)
if err != nil {
log.WithError(err).WithField("path", jwd).Error("Failed to get absolute path")
return "", err
}
if _, err := os.Stat(ajwd); err != nil {
os.MkdirAll(ajwd, 0755)
log.WithField("path", ajwd).Debug("Directory does not exist, creating")
if err := os.MkdirAll(ajwd, 0755); err != nil {
log.WithError(err).WithField("path", ajwd).Error("Failed to create directory")
return "", err
}
}
log.WithField("path", ajwd).Debug("Successfully got joined working directory")
return ajwd, nil
}
@@ -53,68 +61,112 @@ var TLS_KEYSTORE_PATH = tlsdefault
// path is not set, it returns the default path. If the path does
// not exist, it creates it.
func I2PKeystorePath() (string, error) {
log.WithField("path", I2P_KEYSTORE_PATH).Debug("Checking I2P keystore path")
if _, err := os.Stat(I2P_KEYSTORE_PATH); err != nil {
log.WithField("path", I2P_KEYSTORE_PATH).Debug("I2P keystore directory does not exist, creating")
err := os.MkdirAll(I2P_KEYSTORE_PATH, 0755)
if err != nil {
log.WithError(err).WithField("path", I2P_KEYSTORE_PATH).Error("Failed to create I2P keystore directory")
return "", err
}
}
log.WithField("path", I2P_KEYSTORE_PATH).Debug("I2P keystore path verified")
return I2P_KEYSTORE_PATH, nil
}
// DeleteI2PKeyStore deletes the I2P Keystore.
func DeleteI2PKeyStore() error {
return os.RemoveAll(I2P_KEYSTORE_PATH)
log.WithField("path", I2P_KEYSTORE_PATH).Debug("Attempting to delete I2P keystore")
err := os.RemoveAll(I2P_KEYSTORE_PATH)
if err != nil {
log.WithError(err).WithField("path", I2P_KEYSTORE_PATH).Error("Failed to delete I2P keystore")
return err
}
log.WithField("path", I2P_KEYSTORE_PATH).Debug("Successfully deleted I2P keystore")
return nil
//return os.RemoveAll(I2P_KEYSTORE_PATH)
}
// TorKeystorePath returns the path to the Onion Keystore. If the
// path is not set, it returns the default path. If the path does
// not exist, it creates it.
func TorKeystorePath() (string, error) {
log.WithField("path", ONION_KEYSTORE_PATH).Debug("Checking Tor keystore path")
if _, err := os.Stat(ONION_KEYSTORE_PATH); err != nil {
log.WithField("path", ONION_KEYSTORE_PATH).Debug("Tor keystore directory does not exist, creating")
err := os.MkdirAll(ONION_KEYSTORE_PATH, 0755)
if err != nil {
log.WithError(err).WithField("path", ONION_KEYSTORE_PATH).Error("Failed to create Tor keystore directory")
return "", err
}
}
log.WithField("path", ONION_KEYSTORE_PATH).Debug("Tor keystore path verified")
return ONION_KEYSTORE_PATH, nil
}
// DeleteTorKeyStore deletes the Onion Keystore.
func DeleteTorKeyStore() error {
return os.RemoveAll(ONION_KEYSTORE_PATH)
log.WithField("path", ONION_KEYSTORE_PATH).Debug("Attempting to delete Tor keystore")
err := os.RemoveAll(ONION_KEYSTORE_PATH)
if err != nil {
log.WithError(err).WithField("path", ONION_KEYSTORE_PATH).Error("Failed to delete Tor keystore")
return err
}
log.WithField("path", ONION_KEYSTORE_PATH).Debug("Successfully deleted Tor keystore")
return nil
//return os.RemoveAll(ONION_KEYSTORE_PATH)
}
// TLSKeystorePath returns the path to the TLS Keystore. If the
// path is not set, it returns the default path. If the path does
// not exist, it creates it.
func TLSKeystorePath() (string, error) {
log.WithField("path", TLS_KEYSTORE_PATH).Debug("Checking TLS keystore path")
if _, err := os.Stat(TLS_KEYSTORE_PATH); err != nil {
log.WithField("path", TLS_KEYSTORE_PATH).Debug("TLS keystore directory does not exist, creating")
err := os.MkdirAll(TLS_KEYSTORE_PATH, 0755)
if err != nil {
log.WithError(err).WithField("path", TLS_KEYSTORE_PATH).Error("Failed to create TLS keystore directory")
return "", err
}
}
log.WithField("path", TLS_KEYSTORE_PATH).Debug("TLS keystore path verified")
return TLS_KEYSTORE_PATH, nil
}
// DeleteTLSKeyStore deletes the TLS Keystore.
func DeleteTLSKeyStore() error {
return os.RemoveAll(TLS_KEYSTORE_PATH)
log.WithField("path", TLS_KEYSTORE_PATH).Debug("Attempting to delete TLS keystore")
err := os.RemoveAll(TLS_KEYSTORE_PATH)
if err != nil {
log.WithError(err).WithField("path", TLS_KEYSTORE_PATH).Error("Failed to delete TLS keystore")
return err
}
log.WithField("path", TLS_KEYSTORE_PATH).Debug("Successfully deleted TLS keystore")
return nil
//return os.RemoveAll(TLS_KEYSTORE_PATH)
}
// Dial returns a connection for the given network and address.
// network is ignored. If the address ends in i2p, it returns an I2P connection.
// if the address ends in anything else, it returns a Tor connection.
func Dial(network, addr string) (net.Conn, error) {
log.WithFields(logrus.Fields{
"network": network,
"address": addr,
}).Debug("Attempting to dial")
url, err := url.Parse(addr)
if err != nil {
log.WithError(err).WithField("address", addr).Error("Failed to parse address")
return nil, err
}
hostname := url.Hostname()
if strings.HasSuffix(hostname, ".i2p") {
log.WithField("hostname", hostname).Debug("Using I2P connection for .i2p address")
return DialGarlic(network, addr)
}
log.WithField("hostname", hostname).Debug("Using Tor connection for non-i2p address")
return DialOnion(network, addr)
}
@@ -123,19 +175,31 @@ func Dial(network, addr string) (net.Conn, error) {
// if network is tor or onion, it returns an Onion listener.
// if keys ends with ".i2p", it returns an I2P listener.
func Listen(network, keys string) (net.Listener, error) {
log.WithFields(logrus.Fields{
"network": network,
"keys": keys,
}).Debug("Attempting to create listener")
if network == "i2p" || network == "garlic" {
log.Debug("Creating I2P listener based on network type")
return ListenGarlic(network, keys)
}
if network == "tor" || network == "onion" {
log.Debug("Creating Tor listener based on network type")
return ListenOnion(network, keys)
}
url, err := url.Parse(keys)
if err != nil {
log.WithError(err).WithField("keys", keys).Error("Failed to parse keys URL")
return nil, err
}
hostname := url.Hostname()
if strings.HasSuffix(hostname, ".i2p") {
log.WithField("hostname", hostname).Debug("Creating I2P listener based on .i2p hostname")
return ListenGarlic(network, keys)
}
log.WithField("hostname", hostname).Debug("Creating Tor listener for non-i2p hostname")
return ListenOnion(network, keys)
}

233
garlic.go
View File

@@ -7,14 +7,14 @@ import (
"context"
"crypto/tls"
"fmt"
"log"
"github.com/sirupsen/logrus"
"net"
"os"
"path/filepath"
"strings"
"github.com/eyedeekay/i2pkeys"
"github.com/eyedeekay/sam3"
"github.com/go-i2p/i2pkeys"
"github.com/go-i2p/sam3"
)
// Garlic is a ready-made I2P streaming manager. Once initialized it always
@@ -101,27 +101,35 @@ func (g *Garlic) getOptions() []string {
func (g *Garlic) samSession() (*sam3.SAM, error) {
if g.SAM == nil {
log.WithField("address", g.getAddr()).Debug("Creating new SAM session")
var err error
g.SAM, err = sam3.NewSAM(g.getAddr())
if err != nil {
log.WithError(err).Error("Failed to create SAM session")
return nil, fmt.Errorf("onramp samSession: %v", err)
}
log.Debug("SAM session created successfully")
}
return g.SAM, nil
}
func (g *Garlic) setupStreamSession() (*sam3.StreamSession, error) {
if g.StreamSession == nil {
log.WithField("name", g.getName()).Debug("Setting up stream session")
var err error
g.ServiceKeys, err = g.Keys()
if err != nil {
log.WithError(err).Error("Failed to get keys for stream session")
return nil, fmt.Errorf("onramp setupStreamSession: %v", err)
}
log.WithField("address", g.ServiceKeys.Address.Base32()).Debug("Creating stream session with keys")
log.Println("Creating stream session with keys:", g.ServiceKeys.Address.Base32())
g.StreamSession, err = g.SAM.NewStreamSession(g.getName(), *g.ServiceKeys, g.getOptions())
if err != nil {
log.WithError(err).Error("Failed to create stream session")
return nil, fmt.Errorf("onramp setupStreamSession: %v", err)
}
log.Debug("Stream session created successfully")
return g.StreamSession, nil
}
return g.StreamSession, nil
@@ -129,78 +137,131 @@ func (g *Garlic) setupStreamSession() (*sam3.StreamSession, error) {
func (g *Garlic) setupDatagramSession() (*sam3.DatagramSession, error) {
if g.DatagramSession == nil {
log.WithField("name", g.getName()).Debug("Setting up datagram session")
var err error
g.ServiceKeys, err = g.Keys()
if err != nil {
log.WithError(err).Error("Failed to get keys for datagram session")
return nil, fmt.Errorf("onramp setupDatagramSession: %v", err)
}
log.WithField("address", g.ServiceKeys.Address.Base32()).Debug("Creating datagram session with keys")
log.Println("Creating datagram session with keys:", g.ServiceKeys.Address.Base32())
g.DatagramSession, err = g.SAM.NewDatagramSession(g.getName(), *g.ServiceKeys, g.getOptions(), 0)
if err != nil {
log.WithError(err).Error("Failed to create datagram session")
return nil, fmt.Errorf("onramp setupDatagramSession: %v", err)
}
log.Debug("Datagram session created successfully")
return g.DatagramSession, nil
}
log.Debug("Using existing datagram session")
return g.DatagramSession, nil
}
// NewListener returns a net.Listener for the Garlic structure's I2P keys.
// accepts a variable list of arguments, arguments after the first one are ignored.
func (g *Garlic) NewListener(n, addr string) (net.Listener, error) {
return g.Listen(n)
log.WithFields(logrus.Fields{
"network": n,
"address": addr,
"name": g.getName(),
}).Debug("Creating new listener")
listener, err := g.Listen(n)
if err != nil {
log.WithError(err).Error("Failed to create listener")
return nil, err
}
log.Debug("Successfully created listener")
return listener, nil
//return g.Listen(n)
}
// Listen returns a net.Listener for the Garlic structure's I2P keys.
// accepts a variable list of arguments, arguments after the first one are ignored.
func (g *Garlic) Listen(args ...string) (net.Listener, error) {
return g.OldListen(args...)
log.WithFields(logrus.Fields{
"args": args,
"name": g.getName(),
}).Debug("Setting up listener")
listener, err := g.OldListen(args...)
if err != nil {
log.WithError(err).Error("Failed to create listener")
return nil, err
}
log.Debug("Successfully created listener")
return listener, nil
//return g.OldListen(args...)
}
// OldListen returns a net.Listener for the Garlic structure's I2P keys.
// accepts a variable list of arguments, arguments after the first one are ignored.
func (g *Garlic) OldListen(args ...string) (net.Listener, error) {
log.WithField("args", args).Debug("Starting OldListen")
if len(args) > 0 {
if args[0] == "tcp" || args[0] == "tcp6" || args[0] == "st" || args[0] == "st6" {
protocol := args[0]
log.WithField("protocol", protocol).Debug("Checking protocol type")
//if args[0] == "tcp" || args[0] == "tcp6" || args[0] == "st" || args[0] == "st6" {
if protocol == "tcp" || protocol == "tcp6" || protocol == "st" || protocol == "st6" {
log.Debug("Using TCP stream listener")
return g.ListenStream()
} else if args[0] == "udp" || args[0] == "udp6" || args[0] == "dg" || args[0] == "dg6" {
//} else if args[0] == "udp" || args[0] == "udp6" || args[0] == "dg" || args[0] == "dg6" {
} else if protocol == "udp" || protocol == "udp6" || protocol == "dg" || protocol == "dg6" {
log.Debug("Using UDP datagram listener")
pk, err := g.ListenPacket()
if err != nil {
log.WithError(err).Error("Failed to create packet listener")
return nil, err
}
log.Debug("Successfully created datagram session")
return pk.(*sam3.DatagramSession), nil
}
}
log.Debug("No protocol specified, defaulting to stream listener")
return g.ListenStream()
}
// Listen returns a net.Listener for the Garlic structure's I2P keys.
func (g *Garlic) ListenStream() (net.Listener, error) {
log.Debug("Setting up stream listener")
var err error
if g.SAM, err = g.samSession(); err != nil {
log.WithError(err).Error("Failed to create SAM session for stream listener")
return nil, fmt.Errorf("onramp NewGarlic: %v", err)
}
if g.StreamSession, err = g.setupStreamSession(); err != nil {
log.WithError(err).Error("Failed to setup stream session")
return nil, fmt.Errorf("onramp Listen: %v", err)
}
if g.StreamListener == nil {
log.Debug("Creating new stream listener")
g.StreamListener, err = g.StreamSession.Listen()
if err != nil {
log.WithError(err).Error("Failed to create stream listener")
return nil, fmt.Errorf("onramp Listen: %v", err)
}
log.Debug("Stream listener created successfully")
}
return g.StreamListener, nil
}
// ListenPacket returns a net.PacketConn for the Garlic structure's I2P keys.
func (g *Garlic) ListenPacket() (net.PacketConn, error) {
log.Debug("Setting up packet connection")
var err error
if g.SAM, err = g.samSession(); err != nil {
log.WithError(err).Error("Failed to create SAM session for packet connection")
return nil, fmt.Errorf("onramp NewGarlic: %v", err)
}
if g.DatagramSession, err = g.setupDatagramSession(); err != nil {
log.WithError(err).Error("Failed to setup datagram session")
return nil, fmt.Errorf("onramp Listen: %v", err)
}
log.Debug("Packet connection successfully established")
return g.DatagramSession, nil
}
@@ -208,23 +269,33 @@ func (g *Garlic) ListenPacket() (net.PacketConn, error) {
// which also uses TLS either for additional encryption, authentication,
// or browser-compatibility.
func (g *Garlic) ListenTLS(args ...string) (net.Listener, error) {
log.WithField("args", args).Debug("Starting TLS listener")
listener, err := g.Listen(args...)
if err != nil {
log.WithError(err).Error("Failed to create base listener")
return nil, err
}
cert, err := g.TLSKeys()
if err != nil {
log.WithError(err).Error("Failed to get TLS keys")
return nil, fmt.Errorf("onramp ListenTLS: %v", err)
}
if len(args) > 0 {
if args[0] == "tcp" || args[0] == "tcp6" || args[0] == "st" || args[0] == "st6" {
protocol := args[0]
log.WithField("protocol", protocol).Debug("Creating TLS listener for protocol")
//if args[0] == "tcp" || args[0] == "tcp6" || args[0] == "st" || args[0] == "st6" {
if protocol == "tcp" || protocol == "tcp6" || protocol == "st" || protocol == "st6" {
log.Debug("Creating TLS stream listener")
return tls.NewListener(
g.StreamListener,
&tls.Config{
Certificates: []tls.Certificate{cert},
},
), nil
} else if args[0] == "udp" || args[0] == "udp6" || args[0] == "dg" || args[0] == "dg6" {
//} else if args[0] == "udp" || args[0] == "udp6" || args[0] == "dg" || args[0] == "dg6" {
} else if protocol == "udp" || protocol == "udp6" || protocol == "dg" || protocol == "dg6" {
log.Debug("Creating TLS datagram listener")
return tls.NewListener(
g.DatagramSession,
&tls.Config{
@@ -234,8 +305,10 @@ func (g *Garlic) ListenTLS(args ...string) (net.Listener, error) {
}
} else {
log.Debug("No protocol specified, using stream listener")
g.StreamListener = listener.(*sam3.StreamListener)
}
log.Debug("Successfully created TLS listener")
return tls.NewListener(
g.StreamListener,
&tls.Config{
@@ -246,76 +319,143 @@ func (g *Garlic) ListenTLS(args ...string) (net.Listener, error) {
// Dial returns a net.Conn for the Garlic structure's I2P keys.
func (g *Garlic) Dial(net, addr string) (net.Conn, error) {
log.WithFields(logrus.Fields{
"network": net,
"address": addr,
}).Debug("Attempting to dial")
if !strings.Contains(addr, ".i2p") {
log.Debug("Non-I2P address detected, returning null connection")
return &NullConn{}, nil
}
var err error
if g.SAM, err = g.samSession(); err != nil {
log.WithError(err).Error("Failed to create SAM session")
return nil, fmt.Errorf("onramp NewGarlic: %v", err)
}
if g.StreamSession, err = g.setupStreamSession(); err != nil {
log.WithError(err).Error("Failed to setup stream session")
return nil, fmt.Errorf("onramp Dial: %v", err)
}
return g.StreamSession.Dial(net, addr)
log.Debug("Attempting to establish connection")
conn, err := g.StreamSession.Dial(net, addr)
if err != nil {
log.WithError(err).Error("Failed to establish connection")
return nil, err
}
log.Debug("Successfully established connection")
return conn, nil
//return g.StreamSession.Dial(net, addr)
}
// DialContext returns a net.Conn for the Garlic structure's I2P keys.
func (g *Garlic) DialContext(ctx context.Context, net, addr string) (net.Conn, error) {
log.WithFields(logrus.Fields{
"network": net,
"address": addr,
}).Debug("Attempting to dial with context")
if !strings.Contains(addr, ".i2p") {
log.Debug("Non-I2P address detected, returning null connection")
return &NullConn{}, nil
}
var err error
if g.SAM, err = g.samSession(); err != nil {
log.WithError(err).Error("Failed to create SAM session")
return nil, fmt.Errorf("onramp NewGarlic: %v", err)
}
if g.StreamSession, err = g.setupStreamSession(); err != nil {
log.WithError(err).Error("Failed to setup stream session")
return nil, fmt.Errorf("onramp Dial: %v", err)
}
return g.StreamSession.DialContext(ctx, net, addr)
log.Debug("Attempting to establish connection with context")
conn, err := g.StreamSession.DialContext(ctx, net, addr)
if err != nil {
log.WithError(err).Error("Failed to establish connection")
return nil, err
}
log.Debug("Successfully established connection")
return conn, nil
//return g.StreamSession.DialContext(ctx, net, addr)
}
// Close closes the Garlic structure's sessions and listeners.
func (g *Garlic) Close() error {
log.WithField("name", g.getName()).Debug("Closing Garlic sessions")
e1 := g.StreamSession.Close()
var err error
if e1 != nil {
log.WithError(e1).Error("Failed to close stream session")
err = fmt.Errorf("onramp Close: %v", e1)
} else {
log.Debug("Stream session closed successfully")
}
e2 := g.SAM.Close()
if e2 != nil {
log.WithError(e2).Error("Failed to close SAM session")
err = fmt.Errorf("onramp Close: %v %v", e1, e2)
} else {
log.Debug("SAM session closed successfully")
}
if err == nil {
log.Debug("All sessions closed successfully")
}
return err
}
// Keys returns the I2PKeys for the Garlic structure. If none
// exist, they are created and stored.
func (g *Garlic) Keys() (*i2pkeys.I2PKeys, error) {
log.WithFields(logrus.Fields{
"name": g.getName(),
"address": g.getAddr(),
}).Debug("Retrieving I2P keys")
keys, err := I2PKeys(g.getName(), g.getAddr())
if err != nil {
log.WithError(err).Error("Failed to get I2P keys")
return &i2pkeys.I2PKeys{}, fmt.Errorf("onramp Keys: %v", err)
}
log.Debug("Successfully retrieved I2P keys")
return &keys, nil
}
func (g *Garlic) DeleteKeys() error {
return DeleteGarlicKeys(g.getName())
//return DeleteGarlicKeys(g.getName())
log.WithField("name", g.getName()).Debug("Attempting to delete Garlic keys")
err := DeleteGarlicKeys(g.getName())
if err != nil {
log.WithError(err).Error("Failed to delete Garlic keys")
}
log.Debug("Successfully deleted Garlic keys")
return err
}
// NewGarlic returns a new Garlic struct. It is immediately ready to use with
// I2P streaming.
func NewGarlic(tunName, samAddr string, options []string) (*Garlic, error) {
log.WithFields(logrus.Fields{
"tunnel_name": tunName,
"sam_address": samAddr,
"options": options,
}).Debug("Creating new Garlic instance")
g := new(Garlic)
g.name = tunName
g.addr = samAddr
g.opts = options
var err error
if g.SAM, err = g.samSession(); err != nil {
log.WithError(err).Error("Failed to create SAM session")
return nil, fmt.Errorf("onramp NewGarlic: %v", err)
}
if g.StreamSession, err = g.setupStreamSession(); err != nil {
log.WithError(err).Error("Failed to setup stream session")
return nil, fmt.Errorf("onramp NewGarlic: %v", err)
}
log.Debug("Successfully created new Garlic instance")
return g, nil
}
@@ -324,49 +464,74 @@ func NewGarlic(tunName, samAddr string, options []string) (*Garlic, error) {
// This is permanent and irreversible, and will change the onion service
// address.
func DeleteGarlicKeys(tunName string) error {
log.WithField("tunnel_name", tunName).Debug("Attempting to delete Garlic keys")
keystore, err := I2PKeystorePath()
if err != nil {
log.WithError(err).Error("Failed to get keystore path")
return fmt.Errorf("onramp DeleteGarlicKeys: discovery error %v", err)
}
keyspath := filepath.Join(keystore, tunName+".i2p.private")
log.WithField("path", keyspath).Debug("Deleting key file")
if err := os.Remove(keyspath); err != nil {
log.WithError(err).WithField("path", keyspath).Error("Failed to delete key file")
return fmt.Errorf("onramp DeleteGarlicKeys: %v", err)
}
log.Debug("Successfully deleted Garlic keys")
return nil
}
// I2PKeys returns the I2PKeys at the keystore directory for the given
// tunnel name. If none exist, they are created and stored.
func I2PKeys(tunName, samAddr string) (i2pkeys.I2PKeys, error) {
log.WithFields(logrus.Fields{
"tunnel_name": tunName,
"sam_address": samAddr,
}).Debug("Looking up I2P keys")
keystore, err := I2PKeystorePath()
if err != nil {
log.WithError(err).Error("Failed to get keystore path")
return i2pkeys.I2PKeys{}, fmt.Errorf("onramp I2PKeys: discovery error %v", err)
}
keyspath := filepath.Join(keystore, tunName+".i2p.private")
log.WithField("path", keyspath).Debug("Checking for existing keys")
info, err := os.Stat(keyspath)
if info != nil {
if info.Size() == 0 {
log.WithField("path", keyspath).Debug("Keystore empty, will regenerate keys")
log.Println("onramp I2PKeys: keystore empty, re-generating keys")
} else {
log.WithField("path", keyspath).Debug("Found existing keystore")
}
}
if err != nil {
log.WithField("path", keyspath).Debug("Keys not found, generating new keys")
sam, err := sam3.NewSAM(samAddr)
if err != nil {
log.WithError(err).Error("Failed to create SAM connection")
return i2pkeys.I2PKeys{}, fmt.Errorf("onramp I2PKeys: SAM error %v", err)
}
log.Debug("SAM connection established")
keys, err := sam.NewKeys(tunName)
if err != nil {
log.WithError(err).Error("Failed to generate new keys")
return i2pkeys.I2PKeys{}, fmt.Errorf("onramp I2PKeys: keygen error %v", err)
}
log.Debug("New keys generated successfully")
if err = i2pkeys.StoreKeys(keys, keyspath); err != nil {
log.WithError(err).WithField("path", keyspath).Error("Failed to store generated keys")
return i2pkeys.I2PKeys{}, fmt.Errorf("onramp I2PKeys: store error %v", err)
}
log.WithField("path", keyspath).Debug("Successfully stored new keys")
return keys, nil
} else {
log.WithField("path", keyspath).Debug("Loading existing keys")
keys, err := i2pkeys.LoadKeys(keyspath)
if err != nil {
log.WithError(err).WithField("path", keyspath).Error("Failed to load existing keys")
return i2pkeys.I2PKeys{}, fmt.Errorf("onramp I2PKeys: load error %v", err)
}
log.Debug("Successfully loaded existing keys")
return keys, nil
}
}
@@ -376,18 +541,35 @@ var garlics map[string]*Garlic
// CloseAllGarlic closes all garlics managed by the onramp package. It does not
// affect objects instantiated by an app.
func CloseAllGarlic() {
log.WithField("count", len(garlics)).Debug("Closing all Garlic connections")
for i, g := range garlics {
log.WithFields(logrus.Fields{
"index": i,
"name": g.name,
}).Debug("Closing Garlic connection")
log.Println("Closing garlic", g.name)
CloseGarlic(i)
}
log.Debug("All Garlic connections closed")
}
// CloseGarlic closes the Garlic at the given index. It does not affect Garlic
// objects instantiated by an app.
func CloseGarlic(tunName string) {
log.WithField("tunnel_name", tunName).Debug("Attempting to close Garlic connection")
g, ok := garlics[tunName]
if ok {
g.Close()
log.Debug("Found Garlic connection, closing")
//g.Close()
err := g.Close()
if err != nil {
log.WithError(err).Error("Error closing Garlic connection")
} else {
log.Debug("Successfully closed Garlic connection")
}
} else {
log.Debug("No Garlic connection found for tunnel name")
}
}
@@ -399,11 +581,18 @@ var SAM_ADDR = "127.0.0.1:7656"
// corresponding to a structure managed by the onramp library
// and not instantiated by an app.
func ListenGarlic(network, keys string) (net.Listener, error) {
log.WithFields(logrus.Fields{
"network": network,
"keys": keys,
"sam_addr": SAM_ADDR,
}).Debug("Creating new Garlic listener")
g, err := NewGarlic(keys, SAM_ADDR, OPT_DEFAULTS)
if err != nil {
log.WithError(err).Error("Failed to create new Garlic")
return nil, fmt.Errorf("onramp Listen: %v", err)
}
garlics[keys] = g
log.Debug("Successfully created Garlic listener")
return g.Listen()
}
@@ -411,10 +600,26 @@ func ListenGarlic(network, keys string) (net.Listener, error) {
// corresponding to a structure managed by the onramp library
// and not instantiated by an app.
func DialGarlic(network, addr string) (net.Conn, error) {
log.WithFields(logrus.Fields{
"network": network,
"address": addr,
"sam_addr": SAM_ADDR,
}).Debug("Creating new Garlic connection")
g, err := NewGarlic(addr, SAM_ADDR, OPT_DEFAULTS)
if err != nil {
log.WithError(err).Error("Failed to create new Garlic")
return nil, fmt.Errorf("onramp Dial: %v", err)
}
garlics[addr] = g
return g.Dial(network, addr)
log.WithField("address", addr).Debug("Attempting to dial")
conn, err := g.Dial(network, addr)
if err != nil {
log.WithError(err).Error("Failed to dial connection")
return nil, err
}
log.Debug("Successfully established Garlic connection")
return conn, nil
//return g.Dial(network, addr)
}

View File

@@ -1,6 +1,6 @@
package onramp
import "github.com/eyedeekay/sam3"
import "github.com/go-i2p/sam3"
var OPT_DEFAULTS = sam3.Options_Default
var OPT_WIDE = sam3.Options_Wide

View File

@@ -7,7 +7,6 @@ import (
"crypto/tls"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"testing"
@@ -17,7 +16,10 @@ import (
func TestBareGarlic(t *testing.T) {
fmt.Println("TestBareGarlic Countdown")
Sleep(5)
garlic := &Garlic{}
garlic, err := NewGarlic("test123", "localhost:7656", OPT_WIDE)
if err != nil {
t.Error(err)
}
defer garlic.Close()
listener, err := garlic.ListenTLS()
if err != nil {
@@ -29,9 +31,14 @@ func TestBareGarlic(t *testing.T) {
fmt.Fprintf(w, "Hello, %q", r.URL.Path)
})
go Serve(listener)
Sleep(15)
garlic2, err := NewGarlic("test321", "localhost:7656", OPT_WIDE)
if err != nil {
t.Error(err)
}
defer garlic2.Close()
Sleep(60)
transport := http.Transport{
Dial: garlic.Dial,
Dial: garlic2.Dial,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},

15
go.mod
View File

@@ -1,16 +1,17 @@
module github.com/eyedeekay/onramp
module github.com/go-i2p/onramp
go 1.18
require (
github.com/cretz/bine v0.2.0
github.com/eyedeekay/i2pkeys v0.33.0
github.com/eyedeekay/sam3 v0.33.5
github.com/go-i2p/i2pkeys v0.33.10-0.20241113193422-e10de5e60708
github.com/go-i2p/sam3 v0.33.9
github.com/sirupsen/logrus v1.9.3
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
golang.org/x/crypto v0.5.0 // indirect
golang.org/x/net v0.5.0 // indirect
golang.org/x/sys v0.4.0 // indirect
github.com/stretchr/testify v1.8.4 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/net v0.12.0 // indirect
golang.org/x/sys v0.27.0 // indirect
)

84
go.sum
View File

@@ -1,85 +1,39 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eyedeekay/goSam v0.32.31-0.20210122211817-f97683379f23/go.mod h1:UgJnih/LpotwKriwVPOEa6yPDM2NDdVrKfLtS5DOLPE=
github.com/eyedeekay/i2pkeys v0.0.0-20220310052025-204d4ae6dcae/go.mod h1:W9KCm9lqZ+Ozwl3dwcgnpPXAML97+I8Jiht7o5A8YBM=
github.com/eyedeekay/i2pkeys v0.33.0 h1:5SzUyWxNjV6AvYv/WaI8J4dSgAfv7/WEps6pDLe2YSs=
github.com/eyedeekay/i2pkeys v0.33.0/go.mod h1:W9KCm9lqZ+Ozwl3dwcgnpPXAML97+I8Jiht7o5A8YBM=
github.com/eyedeekay/sam3 v0.32.32/go.mod h1:qRA9KIIVxbrHlkj+ZB+OoxFGFgdKeGp1vSgPw26eOVU=
github.com/eyedeekay/sam3 v0.33.5 h1:mY2MmEG4W35AOpG/G7DOdAhFZWRwFxlm+NmIoub1Xnw=
github.com/eyedeekay/sam3 v0.33.5/go.mod h1:sPtlI4cRm7wD0UywOzLPvvdY1G++vBSK3n+jiIGqWlU=
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
github.com/getlantern/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
github.com/getlantern/go-socks5 v0.0.0-20171114193258-79d4dd3e2db5/go.mod h1:kGHRXch95rnGLHjER/GhhFiHvfnqNz7KqWD9kGfATHY=
github.com/getlantern/golog v0.0.0-20201105130739-9586b8bde3a9/go.mod h1:ZyIjgH/1wTCl+B+7yH1DqrWp6MPJqESmwmEQ89ZfhvA=
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o=
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA=
github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd/go.mod h1:wKdY0ikOgzrWSeB9UyBVKPRhjXQ+vTb+BPeJuypUuNE=
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/renameio v1.0.0/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/go-i2p/i2pkeys v0.0.0-20241108200332-e4f5ccdff8c4 h1:LRjaRCzg1ieGKZjELlaIg06Fx04RHzQLsWMYp1H6PQ4=
github.com/go-i2p/i2pkeys v0.0.0-20241108200332-e4f5ccdff8c4/go.mod h1:m5TlHjPZrU5KbTd7Lr+I2rljyC6aJ88HdkeMQXV0U0E=
github.com/go-i2p/i2pkeys v0.33.9/go.mod h1:Apt0rKbwylG37GoTAmovuJvB4lu0yFM2sgfIUefbMK8=
github.com/go-i2p/i2pkeys v0.33.10-0.20241113193422-e10de5e60708 h1:Tiy9IBwi21maNpK74yCdHursJJMkyH7w87tX1nXGWzg=
github.com/go-i2p/i2pkeys v0.33.10-0.20241113193422-e10de5e60708/go.mod h1:m5TlHjPZrU5KbTd7Lr+I2rljyC6aJ88HdkeMQXV0U0E=
github.com/go-i2p/sam3 v0.33.9 h1:3a+gunx75DFc6jxloUZTAVJbdP6736VU1dy2i7I9fKA=
github.com/go-i2p/sam3 v0.33.9/go.mod h1:oDuV145l5XWKKafeE4igJHTDpPwA0Yloz9nyKKh92eo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/riobard/go-x25519 v0.0.0-20190716001027-10cc4d8d0b33/go.mod h1:BjmVxzAnkLeoEbqHEerI4eSw6ua+RaIB0S4jMV21RAs=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.1-2020.1.6/go.mod h1:pyyisuGw24ruLjrr1ddx39WE0y9OooInRzEYLhQB2YY=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

50
log.go Normal file
View File

@@ -0,0 +1,50 @@
package onramp
import (
"github.com/sirupsen/logrus"
"io"
"os"
"strings"
"sync"
)
var (
log *logrus.Logger
once sync.Once
)
func InitializeOnrampLogger() {
once.Do(func() {
log = logrus.New()
// We do not want to log by default
log.SetOutput(io.Discard)
log.SetLevel(logrus.PanicLevel)
// Check if DEBUG_I2P is set
if logLevel := os.Getenv("DEBUG_I2P"); logLevel != "" {
log.SetOutput(os.Stdout)
switch strings.ToLower(logLevel) {
case "debug":
log.SetLevel(logrus.DebugLevel)
case "warn":
log.SetLevel(logrus.WarnLevel)
case "error":
log.SetLevel(logrus.ErrorLevel)
default:
log.SetLevel(logrus.DebugLevel)
}
log.WithField("level", log.GetLevel()).Debug("Logging enabled.")
}
})
}
// GetI2PKeysLogger returns the initialized logger
func GetOnrampLogger() *logrus.Logger {
if log == nil {
InitializeOnrampLogger()
}
return log
}
func init() {
GetOnrampLogger()
}

154
onion.go
View File

@@ -7,8 +7,7 @@ import (
"context"
"crypto/tls"
"fmt"
"io/ioutil"
"log"
"github.com/sirupsen/logrus"
"net"
"os"
"path/filepath"
@@ -66,11 +65,14 @@ func (o *Onion) getDialConf() *tor.DialConf {
func (o *Onion) getTor() *tor.Tor {
if torp == nil {
log.Debug("Initializing new Tor instance")
var err error
torp, err = tor.Start(o.getContext(), o.getStartConf())
if err != nil {
panic(err)
log.WithError(err).Error("Failed to start Tor")
panic(err) //return nil instead?
}
log.Debug("Tor instance started successfully")
}
return torp
}
@@ -79,10 +81,13 @@ func (o *Onion) getDialer() *tor.Dialer {
//if o.Dialer == nil {
//var err error
//o.Dialer, err
log.Debug("Creating new Tor dialer")
dialer, err := o.getTor().Dialer(o.getContext(), o.getDialConf())
if err != nil {
log.WithError(err).Error("Failed to create Tor dialer")
panic(err)
}
log.Debug("Tor dialer created successfully")
//}
//return o.Dialer
return dialer
@@ -106,25 +111,55 @@ func (o *Onion) NewListener(n, addr string) (net.Listener, error) {
// address, and will automatically generate a keypair and store it.
// the args are always ignored
func (o *Onion) Listen(args ...string) (net.Listener, error) {
return o.OldListen(args...)
log.WithFields(logrus.Fields{
"args": args,
"name": o.getName(),
}).Debug("Setting up Onion listener")
listener, err := o.OldListen(args...)
if err != nil {
log.WithError(err).Error("Failed to create Onion listener")
return nil, err
}
log.Debug("Successfully created Onion listener")
return listener, nil
//return o.OldListen(args...)
}
// OldListen returns a net.Listener which will listen on an onion
// address, and will automatically generate a keypair and store it.
// the args are always ignored
func (o *Onion) OldListen(args ...string) (net.Listener, error) {
return o.getTor().Listen(o.getContext(), o.getListenConf())
log.WithField("name", o.getName()).Debug("Creating Tor listener")
listener, err := o.getTor().Listen(o.getContext(), o.getListenConf())
if err != nil {
log.WithError(err).Error("Failed to create Tor listener")
return nil, err
}
log.Debug("Successfully created Tor listener")
return listener, nil
//return o.getTor().Listen(o.getContext(), o.getListenConf())
}
// ListenTLS returns a net.Listener which will apply TLS encryption
// to the onion listener, which will not be decrypted until it reaches
// the browser
func (o *Onion) ListenTLS(args ...string) (net.Listener, error) {
log.WithField("args", args).Debug("Setting up TLS Onion listener")
cert, err := o.TLSKeys()
if err != nil {
log.WithError(err).Error("Failed to get TLS keys")
return nil, fmt.Errorf("onramp ListenTLS: %v", err)
}
log.Debug("Creating base Tor listener")
l, err := o.getTor().Listen(o.getContext(), o.getListenConf())
if err != nil {
log.WithError(err).Error("Failed to create base Tor listener")
return nil, err
}
log.Debug("Wrapping Tor listener with TLS")
return tls.NewListener(
l,
&tls.Config{
@@ -135,23 +170,56 @@ func (o *Onion) ListenTLS(args ...string) (net.Listener, error) {
// Dial returns a net.Conn to the given onion address or clearnet address.
func (o *Onion) Dial(net, addr string) (net.Conn, error) {
return o.getDialer().DialContext(o.getContext(), net, addr)
log.WithFields(logrus.Fields{
"network": net,
"address": addr,
}).Debug("Attempting to dial via Tor")
conn, err := o.getDialer().DialContext(o.getContext(), net, addr)
if err != nil {
log.WithError(err).Error("Failed to establish Tor connection")
return nil, err
}
log.Debug("Successfully established Tor connection")
return conn, nil
//return o.getDialer().DialContext(o.getContext(), net, addr)
}
// Close closes the Onion Service and all associated resources.
func (o *Onion) Close() error {
return o.getTor().Close()
log.WithField("name", o.getName()).Debug("Closing Onion service")
err := o.getTor().Close()
if err != nil {
log.WithError(err).Error("Failed to close Tor instance")
return err
}
log.Debug("Successfully closed Onion service")
return nil
//return o.getTor().Close()
}
// Keys returns the keys for the Onion
func (o *Onion) Keys() (ed25519.KeyPair, error) {
return TorKeys(o.getName())
log.WithField("name", o.getName()).Debug("Retrieving Onion keys")
keys, err := TorKeys(o.getName())
if err != nil {
log.WithError(err).Error("Failed to get Tor keys")
return nil, err
}
log.Debug("Successfully retrieved Onion keys")
return keys, nil
//return TorKeys(o.getName())
}
// DeleteKeys deletes the keys at the given key name in the key store.
// This is permanent and irreversible, and will change the onion service
// address.
func (g *Onion) DeleteKeys() error {
log.WithField("Onion keys", g.getName()).Debug("Deleting Onion keys")
return DeleteOnionKeys(g.getName())
}
@@ -166,36 +234,50 @@ func NewOnion(name string) (*Onion, error) {
// name in the key store. If the key already exists, it will be
// returned. If it does not exist, it will be generated.
func TorKeys(keyName string) (ed25519.KeyPair, error) {
log.WithField("key_name", keyName).Debug("Getting Tor keys")
keystore, err := TorKeystorePath()
if err != nil {
log.WithError(err).Error("Failed to get keystore path")
return nil, fmt.Errorf("onramp OnionKeys: discovery error %v", err)
}
var keys ed25519.KeyPair
keysPath := filepath.Join(keystore, keyName+".tor.private")
log.WithField("path", keysPath).Debug("Checking for existing keys")
if _, err := os.Stat(keysPath); os.IsNotExist(err) {
log.Debug("Generating new Tor keys")
tkeys, err := ed25519.GenerateKey(nil)
if err != nil {
log.Fatalf("Unable to generate onion service key, %s", err)
log.WithError(err).Error("Failed to generate onion service key")
log.Fatal("Unable to generate onion service key")
}
keys = tkeys
log.WithField("path", keysPath).Debug("Creating key file")
f, err := os.Create(keysPath)
if err != nil {
log.Fatalf("Unable to create Tor keys file for writing, %s", err)
log.WithError(err).Error("Failed to create Tor keys file")
log.Fatal("Unable to create Tor keys file for writing")
}
defer f.Close()
_, err = f.Write(tkeys.PrivateKey())
if err != nil {
log.Fatalf("Unable to write Tor keys to disk, %s", err)
log.WithError(err).Error("Failed to write Tor keys to disk")
log.Fatal("Unable to write Tor keys to disk")
}
log.Debug("Successfully generated and stored new keys")
} else if err == nil {
tkeys, err := ioutil.ReadFile(keysPath)
log.Debug("Loading existing Tor keys")
tkeys, err := os.ReadFile(keysPath)
if err != nil {
log.Fatalf("Unable to read Tor keys from disk")
log.WithError(err).Error("Failed to read Tor keys from disk")
log.Fatal("Unable to read Tor keys from disk")
}
k := ed25519.FromCryptoPrivateKey(tkeys)
keys = k
log.Debug("Successfully loaded existing keys")
} else {
log.Fatalf("Unable to set up Tor keys, %s", err)
log.WithError(err).Error("Failed to set up Tor keys")
log.Fatal("Unable to set up Tor keys")
}
return keys, nil
}
@@ -205,18 +287,34 @@ var onions map[string]*Onion
// CloseAllOnion closes all onions managed by the onramp package. It does not
// affect objects instantiated by an app.
func CloseAllOnion() {
log.WithField("count", len(onions)).Debug("Closing all Onion services")
for i, g := range onions {
log.Println("Closing onion", g.name)
log.WithFields(logrus.Fields{
"index": i,
"name": g.name,
}).Debug("Closing Onion service")
CloseOnion(i)
}
log.Debug("All Onion services closed")
}
// CloseOnion closes the Onion at the given index. It does not affect Onion
// objects instantiated by an app.
func CloseOnion(tunName string) {
log.WithField("tunnel_name", tunName).Debug("Attempting to close Onion service")
g, ok := onions[tunName]
if ok {
g.Close()
log.WithField("name", g.name).Debug("Found Onion service, closing")
err := g.Close()
if err != nil {
log.WithError(err).Error("Failed to close Onion service")
} else {
log.Debug("Successfully closed Onion service")
}
} else {
log.Debug("No Onion service found for tunnel name")
}
}
@@ -224,12 +322,28 @@ func CloseOnion(tunName string) {
// corresponding to a structure managed by the onramp library
// and not instantiated by an app.
func ListenOnion(network, keys string) (net.Listener, error) {
log.WithFields(logrus.Fields{
"network": network,
"keys": keys,
}).Debug("Creating new Onion listener")
g, err := NewOnion(keys)
if err != nil {
log.WithError(err).Error("Failed to create new Onion")
return nil, fmt.Errorf("onramp Listen: %v", err)
}
onions[keys] = g
return g.Listen()
log.Debug("Onion service registered, creating listener")
listener, err := g.Listen()
if err != nil {
log.WithError(err).Error("Failed to create Onion listener")
return nil, err
}
log.Debug("Successfully created Onion listener")
return listener, nil
//return g.Listen()
}
// DialOnion returns a net.Conn for a onion structure's keys
@@ -247,13 +361,19 @@ func DialOnion(network, addr string) (net.Conn, error) {
// DeleteOnionKeys deletes the key file at the given path as determined by
// keystore + tunName.
func DeleteOnionKeys(tunName string) error {
log.WithField("tunnel_name", tunName).Debug("Attempting to delete Onion keys")
keystore, err := TorKeystorePath()
if err != nil {
log.WithError(err).Error("Failed to get keystore path")
return fmt.Errorf("onramp DeleteOnionKeys: discovery error %v", err)
}
keyspath := filepath.Join(keystore, tunName+".i2p.private")
log.WithError(err).Error("Failed to get keystore path")
if err := os.Remove(keyspath); err != nil {
log.WithError(err).WithField("path", keyspath).Error("Failed to delete key file")
return fmt.Errorf("onramp DeleteOnionKeys: %v", err)
}
log.Debug("Successfully deleted Onion keys")
return nil
}

View File

@@ -7,7 +7,6 @@ import (
"crypto/tls"
"fmt"
"io/ioutil"
"log"
"net/http"
"testing"
)
@@ -15,7 +14,10 @@ import (
func TestBareOnion(t *testing.T) {
fmt.Println("TestBareOnion Countdown")
Sleep(5)
onion := &Onion{}
onion, err := NewOnion("test123")
if err != nil {
t.Error(err)
}
defer onion.Close()
listener, err := onion.ListenTLS()
if err != nil {
@@ -27,7 +29,7 @@ func TestBareOnion(t *testing.T) {
fmt.Fprintf(w, "Hello, %q", r.URL.Path)
})
go Serve(listener)
Sleep(15)
Sleep(60)
transport := http.Transport{
Dial: onion.Dial,
TLSClientConfig: &tls.Config{

View File

@@ -1,8 +1,8 @@
package onramp
import (
"github.com/sirupsen/logrus"
"io"
"log"
"net"
"strings"
)
@@ -21,30 +21,59 @@ type OnrampProxy struct {
// and an I2P or Onion address, and it will act as a tunnel to a
// listening hidden service somewhere.
func (p *OnrampProxy) Proxy(list net.Listener, raddr string) error {
log.WithFields(logrus.Fields{
"remote_address": raddr,
"local_address": list.Addr().String(),
}).Debug("Starting proxy service")
for {
log.Debug("Waiting for incoming connection")
conn, err := list.Accept()
if err != nil {
log.WithError(err).Error("Failed to accept connection")
return err
}
log.WithFields(logrus.Fields{
"local_addr": conn.LocalAddr().String(),
"remote_addr": conn.RemoteAddr().String(),
}).Debug("Accepted new connection, starting proxy routine")
go p.proxy(conn, raddr)
}
}
func (p *OnrampProxy) proxy(conn net.Conn, raddr string) {
log.WithFields(logrus.Fields{
"remote_address": raddr,
"local_addr": conn.LocalAddr().String(),
"remote_addr": conn.RemoteAddr().String(),
}).Debug("Setting up proxy connection")
var remote net.Conn
var err error
checkaddr := strings.Split(raddr, ":")[0]
if strings.HasSuffix(checkaddr, ".i2p") {
log.Debug("Detected I2P address, using Garlic connection")
remote, err = p.Garlic.Dial("tcp", raddr)
} else if strings.HasSuffix(checkaddr, ".onion") {
log.Debug("Detected Onion address, using Tor connection")
remote, err = p.Onion.Dial("tcp", raddr)
} else {
log.Debug("Using standard TCP connection")
remote, err = net.Dial("tcp", raddr)
}
if err != nil {
log.Fatalf("cannot dial to remote: %v", err)
log.WithError(err).Error("Failed to establish remote connection")
log.Fatal("Cannot dial to remote")
}
defer remote.Close()
log.WithFields(logrus.Fields{
"local_addr": remote.LocalAddr().String(),
"remote_addr": remote.RemoteAddr().String(),
}).Debug("Remote connection established, starting bidirectional copy")
go io.Copy(remote, conn)
io.Copy(conn, remote)
}

55
tls.go
View File

@@ -10,6 +10,7 @@ import (
"encoding/asn1"
"encoding/pem"
"fmt"
"github.com/sirupsen/logrus"
"math/big"
"net"
"os"
@@ -24,11 +25,14 @@ import (
// if no TLS keys exist, they will be generated. They will be valid for
// the .b32.i2p domain.
func (g *Garlic) TLSKeys() (tls.Certificate, error) {
log.WithField("name", g.getName()).Debug("Getting TLS keys for Garlic service")
keys, err := g.Keys()
if err != nil {
log.WithError(err).Error("Failed to get I2P keys")
return tls.Certificate{}, err
}
base32 := keys.Addr().Base32()
log.WithField("base32", base32).Debug("Retrieving TLS certificate for base32 address")
return TLSKeys(base32)
}
@@ -36,31 +40,45 @@ func (g *Garlic) TLSKeys() (tls.Certificate, error) {
// if no TLS keys exist, they will be generated. They will be valid for
// the .onion domain.
func (o *Onion) TLSKeys() (tls.Certificate, error) {
log.WithField("name", o.getName()).Debug("Getting TLS keys for Onion service")
keys, err := o.Keys()
if err != nil {
return tls.Certificate{}, err
}
onionService := torutil.OnionServiceIDFromPrivateKey(keys.PrivateKey)
onionService := torutil.OnionServiceIDFromPrivateKey(keys)
log.WithField("onion_service", onionService).Debug("Retrieving TLS certificate for onion service")
return TLSKeys(onionService)
}
// TLSKeys returns the TLS certificate and key for the given hostname.
func TLSKeys(tlsHost string) (tls.Certificate, error) {
log.WithField("host", tlsHost).Debug("Getting TLS certificate and key")
tlsCert := tlsHost + ".crt"
tlsKey := tlsHost + ".pem"
if err := CreateTLSCertificate(tlsHost); nil != err {
log.WithError(err).Error("Failed to create TLS certificate")
return tls.Certificate{}, err
}
tlsKeystorePath, err := TLSKeystorePath()
if err != nil {
log.WithError(err).Error("Failed to get TLS keystore path")
return tls.Certificate{}, err
}
tlsCertPath := filepath.Join(tlsKeystorePath, tlsCert)
tlsKeyPath := filepath.Join(tlsKeystorePath, tlsKey)
log.WithFields(logrus.Fields{
"cert_path": tlsCertPath,
"key_path": tlsKeyPath,
}).Debug("Loading TLS certificate pair")
cert, err := tls.LoadX509KeyPair(tlsCertPath, tlsKeyPath)
if err != nil {
log.WithError(err).Error("Failed to load TLS certificate pair")
return cert, err
}
log.Debug("Successfully loaded TLS certificate and key")
return cert, nil
}
@@ -68,6 +86,7 @@ func TLSKeys(tlsHost string) (tls.Certificate, error) {
// and stores it in the TLS keystore for the application. If the keys already
// exist, generation is skipped.
func CreateTLSCertificate(tlsHost string) error {
log.WithField("host", tlsHost).Debug("Creating TLS certificate")
tlsCertName := tlsHost + ".crt"
tlsKeyName := tlsHost + ".pem"
tlsKeystorePath, err := TLSKeystorePath()
@@ -79,70 +98,102 @@ func CreateTLSCertificate(tlsHost string) error {
_, certErr := os.Stat(tlsCert)
_, keyErr := os.Stat(tlsKey)
if certErr != nil || keyErr != nil {
log.WithFields(logrus.Fields{
"cert_exists": certErr == nil,
"key_exists": keyErr == nil,
"cert_path": tlsCert,
"key_path": tlsKey,
}).Debug("Certificate or key missing, generating new ones")
if certErr != nil {
log.WithField("path", tlsCert).Debug("TLS certificate not found")
fmt.Printf("Unable to read TLS certificate '%s'\n", tlsCert)
}
if keyErr != nil {
log.WithField("path", tlsKey).Debug("TLS key not found")
fmt.Printf("Unable to read TLS key '%s'\n", tlsKey)
}
if err := createTLSCertificate(tlsHost); nil != err {
log.WithError(err).Error("Failed to create TLS certificate")
return err
}
} else {
log.Debug("TLS certificate and key already exist")
}
return nil
}
func createTLSCertificate(host string) error {
log.WithField("host", host).Debug("Generating new TLS certificate")
fmt.Println("Generating TLS keys. This may take a minute...")
priv, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
if err != nil {
log.WithError(err).Error("Failed to generate private key")
return err
}
tlsCert, err := NewTLSCertificate(host, priv)
if nil != err {
log.WithError(err).Error("Failed to create new TLS certificate")
return err
}
privStore, err := TLSKeystorePath()
if nil != err {
log.WithError(err).Error("Failed to get keystore path")
return err
}
certFile := filepath.Join(privStore, host+".crt")
log.WithField("path", certFile).Debug("Saving TLS certificate")
// save the TLS certificate
certOut, err := os.Create(certFile)
if err != nil {
log.WithError(err).WithField("path", certFile).Error("Failed to create certificate file")
return fmt.Errorf("failed to open %s for writing: %s", host+".crt", err)
}
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: tlsCert})
certOut.Close()
log.WithField("path", certFile).Debug("TLS certificate saved successfully")
fmt.Printf("\tTLS certificate saved to: %s\n", host+".crt")
// save the TLS private key
privFile := filepath.Join(privStore, host+".pem")
log.WithField("path", privFile).Debug("Saving TLS private key")
keyOut, err := os.OpenFile(privFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
log.WithError(err).WithField("path", privFile).Error("Failed to create private key file")
return fmt.Errorf("failed to open %s for writing: %v", privFile, err)
}
secp384r1, err := asn1.Marshal(asn1.ObjectIdentifier{1, 3, 132, 0, 34}) // http://www.ietf.org/rfc/rfc5480.txt
if err != nil {
log.WithError(err).Error("Failed to marshal EC parameters")
return err
}
pem.Encode(keyOut, &pem.Block{Type: "EC PARAMETERS", Bytes: secp384r1})
ecder, err := x509.MarshalECPrivateKey(priv)
if err != nil {
log.WithError(err).Error("Failed to marshal private key")
return err
}
pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: ecder})
pem.Encode(keyOut, &pem.Block{Type: "CERTIFICATE", Bytes: tlsCert})
keyOut.Close()
log.WithField("path", privFile).Debug("TLS private key saved successfully")
fmt.Printf("\tTLS private key saved to: %s\n", privFile)
// CRL
crlFile := filepath.Join(privStore, host+".crl")
log.WithField("path", crlFile).Debug("Creating CRL")
crlOut, err := os.OpenFile(crlFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
log.WithError(err).WithField("path", crlFile).Error("Failed to create CRL file")
return fmt.Errorf("failed to open %s for writing: %s", crlFile, err)
}
crlcert, err := x509.ParseCertificate(tlsCert)
if err != nil {
log.WithError(err).Error("Failed to parse certificate for CRL creation")
return fmt.Errorf("Certificate with unknown critical extension was not parsed: %s", err)
}
@@ -156,10 +207,12 @@ func createTLSCertificate(host string) error {
crlBytes, err := crlcert.CreateCRL(rand.Reader, priv, revokedCerts, now, now)
if err != nil {
log.WithError(err).Error("Failed to create CRL")
return fmt.Errorf("error creating CRL: %s", err)
}
_, err = x509.ParseDERCRL(crlBytes)
if err != nil {
log.WithError(err).Error("Failed to validate generated CRL")
return fmt.Errorf("error reparsing CRL: %s", err)
}
pem.Encode(crlOut, &pem.Block{Type: "X509 CRL", Bytes: crlBytes})