Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swarm Addrs, Disable secio opt, + tests #1399

Merged
merged 9 commits into from
Jun 27, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 31 additions & 4 deletions cmd/ipfs/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/http"
_ "net/http/pprof"
"os"
"sort"
"strings"
"sync"

Expand All @@ -18,6 +19,7 @@ import (
commands "github.com/ipfs/go-ipfs/core/commands"
corehttp "github.com/ipfs/go-ipfs/core/corehttp"
"github.com/ipfs/go-ipfs/core/corerouting"
conn "github.com/ipfs/go-ipfs/p2p/net/conn"
peer "github.com/ipfs/go-ipfs/p2p/peer"
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
util "github.com/ipfs/go-ipfs/util"
Expand All @@ -31,7 +33,8 @@ const (
writableKwd = "writable"
ipfsMountKwd = "mount-ipfs"
ipnsMountKwd = "mount-ipns"
unrestrictedApiAccess = "unrestricted-api"
unrestrictedApiAccessKwd = "unrestricted-api"
unencryptTransportKwd = "disable-transport-encryption"
// apiAddrKwd = "address-api"
// swarmAddrKwd = "address-swarm"
)
Expand Down Expand Up @@ -75,7 +78,8 @@ the port as you would other services or database (firewall, authenticated proxy,
cmds.BoolOption(writableKwd, "Enable writing objects (with POST, PUT and DELETE)"),
cmds.StringOption(ipfsMountKwd, "Path to the mountpoint for IPFS (if using --mount)"),
cmds.StringOption(ipnsMountKwd, "Path to the mountpoint for IPNS (if using --mount)"),
cmds.BoolOption(unrestrictedApiAccess, "Allow API access to unlisted hashes"),
cmds.BoolOption(unrestrictedApiAccessKwd, "Allow API access to unlisted hashes"),
cmds.BoolOption(unencryptTransportKwd, "Disable transport encryption (for debugging protocols)"),

// TODO: add way to override addresses. tricky part: updating the config if also --init.
// cmds.StringOption(apiAddrKwd, "Address for the daemon rpc API (overrides config)"),
Expand Down Expand Up @@ -109,6 +113,14 @@ func daemonFunc(req cmds.Request, res cmds.Response) {
}
}()

// check transport encryption flag.
unencrypted, _, _ := req.Option(unencryptTransportKwd).Bool()
if unencrypted {
log.Warningf(`Running with --%s: All connections are UNENCRYPTED.
You will not be able to connect to regular encrypted networks.`, unencryptTransportKwd)
conn.EncryptConnections = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exposed package globals like this frighten me. Will we remove this once node interop works?

If it stays around, I'd prefer conn.DisableEncryption() and print a warning on each dial as well. Found it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd still prefer if this was an opt-in for the lifetime of the process. No funky on/off switching.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exposed package globals like this frighten me. Will we remove this once node interop works?
If it stays around, I'd prefer conn.DisableEncryption()

I imagine with a function we can also make it one-way-- i.e. can only disable it or something.

btw, nothing prevents people from writing different implementations -- or, if they have access to the process, overwrite random memory. Even without this switch, if you can manipulate the process everything's done.

I'd still prefer if this was an opt-in for the lifetime of the process. No funky on/off switching.

yeah it is

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just want to guard (package) users from using this in unintended ways and make it clear to them that this is a development feature. A func could disallow you to switch encryption back on.

The rest is fine by me. Of course we can't do anything meaningful about corruption from the outside.

}

// first, whether user has provided the initialization flag. we may be
// running in an uninitialized state.
initialize, _, err := req.Option(initOptionKwd).Bool()
Expand Down Expand Up @@ -179,6 +191,8 @@ func daemonFunc(req cmds.Request, res cmds.Response) {
return
}

printSwarmAddrs(node)

defer func() {
// We wait for the node to close first, as the node has children
// that it will wait for before closing, such as the API server.
Expand Down Expand Up @@ -256,9 +270,9 @@ func serveHTTPApi(req cmds.Request) (error, <-chan error) {
apiMaddr = apiLis.Multiaddr()
fmt.Printf("API server listening on %s\n", apiMaddr)

unrestricted, _, err := req.Option(unrestrictedApiAccess).Bool()
unrestricted, _, err := req.Option(unrestrictedApiAccessKwd).Bool()
if err != nil {
return fmt.Errorf("serveHTTPApi: Option(%s) failed: %s", unrestrictedApiAccess, err), nil
return fmt.Errorf("serveHTTPApi: Option(%s) failed: %s", unrestrictedApiAccessKwd, err), nil
}

apiGw := corehttp.NewGateway(corehttp.GatewayConfig{
Expand Down Expand Up @@ -305,6 +319,19 @@ func serveHTTPApi(req cmds.Request) (error, <-chan error) {
return nil, errc
}

// printSwarmAddrs prints the addresses of the host
func printSwarmAddrs(node *core.IpfsNode) {
var addrs []string
for _, addr := range node.PeerHost.Addrs() {
addrs = append(addrs, addr.String())
}
sort.Sort(sort.StringSlice(addrs))

for _, addr := range addrs {
fmt.Printf("Swarm listening on %s\n", addr)
}
}

// serveHTTPGateway collects options, creates listener, prints status message and starts serving requests
func serveHTTPGateway(req cmds.Request) (error, <-chan error) {
cfg, err := req.Context().GetConfig()
Expand Down
4 changes: 4 additions & 0 deletions core/commands/id.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ ipfs id supports the format option for output with the following keys:
<aver>: agent version
<pver>: protocol version
<pubkey>: public key
<addrs>: addresses (newline delimited)
`,
},
Arguments: []cmds.Argument{
Expand Down Expand Up @@ -119,6 +120,9 @@ ipfs id supports the format option for output with the following keys:
output = strings.Replace(output, "<aver>", val.AgentVersion, -1)
output = strings.Replace(output, "<pver>", val.ProtocolVersion, -1)
output = strings.Replace(output, "<pubkey>", val.PublicKey, -1)
output = strings.Replace(output, "<addrs>", strings.Join(val.Addresses, "\n"), -1)
output = strings.Replace(output, "\\n", "\n", -1)
output = strings.Replace(output, "\\t", "\t", -1)
return strings.NewReader(output), nil
} else {

Expand Down
48 changes: 48 additions & 0 deletions core/commands/swarm.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io"
"path"
"sort"

cmds "github.com/ipfs/go-ipfs/commands"
Expand Down Expand Up @@ -91,6 +92,9 @@ var swarmAddrsCmd = &cmds.Command{
ipfs swarm addrs lists all addresses this node is aware of.
`,
},
Subcommands: map[string]*cmds.Command{
"local": swarmAddrsLocalCmd,
},
Run: func(req cmds.Request, res cmds.Response) {

n, err := req.Context().GetNode()
Expand Down Expand Up @@ -144,6 +148,50 @@ ipfs swarm addrs lists all addresses this node is aware of.
Type: addrMap{},
}

var swarmAddrsLocalCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "List local addresses.",
ShortDescription: `
ipfs swarm addrs local lists all local addresses the node is listening on.
`,
},
Options: []cmds.Option{
cmds.BoolOption("id", "Show peer ID in addresses"),
},
Run: func(req cmds.Request, res cmds.Response) {

n, err := req.Context().GetNode()
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}

if n.PeerHost == nil {
res.SetError(errNotOnline, cmds.ErrClient)
return
}

showid, _, _ := req.Option("id").Bool()
id := n.Identity.Pretty()

var addrs []string
for _, addr := range n.PeerHost.Addrs() {
saddr := addr.String()
if showid {
saddr = path.Join(saddr, "ipfs", id)
}
addrs = append(addrs, saddr)
}
sort.Sort(sort.StringSlice(addrs))

res.SetOutput(&stringList{addrs})
},
Type: stringList{},
Marshalers: cmds.MarshalerMap{
cmds.Text: stringListMarshaler,
},
}

var swarmConnectCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Open connection to a given address",
Expand Down
2 changes: 1 addition & 1 deletion p2p/net/conn/dial.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (d *Dialer) Dial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (
return
}

if d.PrivateKey == nil {
if d.PrivateKey == nil || EncryptConnections == false {
log.Warning("dialer %s dialing INSECURELY %s at %s!", d, remote, raddr)
connOut = c
return
Expand Down
8 changes: 8 additions & 0 deletions p2p/net/conn/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,11 @@ type Listener interface {
// Any blocked Accept operations will be unblocked and return errors.
Close() error
}

// EncryptConnections is a global parameter because it should either be
// enabled or _completely disabled_. I.e. a node should only be able to talk
// to proper (encrypted) networks if it is encrypting all its transports.
// Running a node with disabled transport encryption is useful to debug the
// protocols, achieve implementation interop, or for private networks which
// -- for whatever reason -- _must_ run unencrypted.
var EncryptConnections = true
2 changes: 1 addition & 1 deletion p2p/net/conn/listen.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (l *listener) Accept() (net.Conn, error) {
return nil, err
}

if l.privk == nil {
if l.privk == nil || EncryptConnections == false {
log.Warning("listener %s listening INSECURELY!", l)
return c, nil
}
Expand Down
36 changes: 21 additions & 15 deletions p2p/net/mock/mock_notif_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,30 @@ func TestNotifications(t *testing.T) {
for i, s := range nets {
n := notifiees[i]
for _, s2 := range nets {
cos := s.ConnsToPeer(s2.LocalPeer())
func() {
for i := 0; i < len(cos); i++ {
var c inet.Conn
select {
case c = <-n.connected:
case <-time.After(timeout):
t.Fatal("timeout")
}
for _, c2 := range cos {
if c == c2 {
t.Log("got notif for conn")
return
}
var actual []inet.Conn
for len(s.ConnsToPeer(s2.LocalPeer())) != len(actual) {
select {
case c := <-n.connected:
actual = append(actual, c)
case <-time.After(timeout):
t.Fatal("timeout")
}
}

expect := s.ConnsToPeer(s2.LocalPeer())
for _, c1 := range actual {
found := false
for _, c2 := range expect {
if c1 == c2 {
found = true
break
}
}
if !found {
t.Error("connection not found")
}
}()
}

}
}

Expand Down
40 changes: 22 additions & 18 deletions p2p/net/swarm/swarm_notif_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import (
)

func TestNotifications(t *testing.T) {
t.Parallel()

ctx := context.Background()
swarms := makeSwarms(ctx, t, 5)
defer func() {
Expand Down Expand Up @@ -44,24 +42,30 @@ func TestNotifications(t *testing.T) {
continue
}

cos := s.ConnectionsToPeer(s2.LocalPeer())
func() {
for i := 0; i < len(cos); i++ {
var c inet.Conn
select {
case c = <-n.connected:
case <-time.After(timeout):
t.Fatal("timeout")
}
for _, c2 := range cos {
if c == c2 {
t.Log("got notif for conn", c)
return
}
var actual []inet.Conn
for len(s.ConnectionsToPeer(s2.LocalPeer())) != len(actual) {
select {
case c := <-n.connected:
actual = append(actual, c)
case <-time.After(timeout):
t.Fatal("timeout")
}
}

expect := s.ConnectionsToPeer(s2.LocalPeer())
for _, c1 := range actual {
found := false
for _, c2 := range expect {
if c1 == c2 {
found = true
break
}
t.Error("connection not found", c)
}
}()
if !found {
t.Error("connection not found")
}
}

}
}

Expand Down
2 changes: 1 addition & 1 deletion p2p/net/swarm/swarm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,6 @@ func TestFilterBounds(t *testing.T) {
case <-time.After(time.Second):
t.Fatal("should have gotten connection")
case <-conns:
fmt.Println("got connect")
t.Log("got connect")
}
}
4 changes: 3 additions & 1 deletion test/sharness/lib/test-lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,10 @@ test_config_ipfs_gateway_writable() {

test_launch_ipfs_daemon() {

args=$1

test_expect_success "'ipfs daemon' succeeds" '
ipfs daemon >actual_daemon 2>daemon_err &
ipfs daemon $args >actual_daemon 2>daemon_err &
'

# we say the daemon is ready when the API server is ready.
Expand Down
19 changes: 18 additions & 1 deletion test/sharness/t0060-daemon.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ test_description="Test daemon command"

. lib/test-lib.sh

# TODO: randomize ports here once we add --config to ipfs daemon

# this needs to be in a different test than "ipfs daemon --init" below
test_expect_success "setup IPFS_PATH" '
IPFS_PATH="$(pwd)/.ipfs" &&
Expand All @@ -17,7 +19,7 @@ test_expect_success "setup IPFS_PATH" '
# NOTE: this should remove bootstrap peers (needs a flag)
# TODO(cryptix):
# - we won't see daemon startup failure because we put the daemon in the background - fix: fork with exit code after api listen
# - also default ports: might clash with local clients. Failure in that case isn't clear as well because pollEndpoint just uses the already running node
# - also default ports: might clash with local clients. Failure in that case isn't clear as well because pollEndpoint just uses the already running node
test_expect_success "ipfs daemon --init launches" '
ipfs daemon --init >actual_daemon 2>daemon_err &
'
Expand All @@ -34,6 +36,11 @@ test_expect_success "'ipfs config Identity.PeerID' works" '
ipfs config Identity.PeerID >config_peerId
'

test_expect_success "'ipfs swarm addrs local' works" '
ipfs swarm addrs local >local_addrs
'


# this is lifted straight from t0020-init.sh
test_expect_success "ipfs peer id looks good" '
PEERID=$(cat config_peerId) &&
Expand All @@ -58,6 +65,7 @@ test_expect_success "ipfs daemon output looks good" '
echo "peer identity: $PEERID" >>expected_daemon &&
echo "to get started, enter:" >>expected_daemon &&
printf "\\n\\t$STARTFILE\\n\\n" >>expected_daemon &&
cat local_addrs | sed "s/^/Swarm listening on /" >>expected_daemon &&
echo "API server listening on /ip4/127.0.0.1/tcp/5001" >>expected_daemon &&
echo "Gateway (readonly) server listening on /ip4/127.0.0.1/tcp/8080" >>expected_daemon &&
test_cmp expected_daemon actual_daemon
Expand Down Expand Up @@ -91,6 +99,15 @@ test_expect_success "ipfs help output looks good" '
test_fsh cat help.txt
'

# check transport is encrypted

test_expect_success 'transport should be encrypted' '
nc -w 5 localhost 4001 >swarmnc &&
grep -q "AES-256,AES-128" swarmnc &&
test_must_fail grep -q "/ipfs/identify" swarmnc ||
test_fsh cat swarmnc
'

# end same as in t0010

test_expect_success "daemon is still running" '
Expand Down
Loading