|
4 | 4 | [](https://pkg.go.dev/github.com/tonymet/dualstack) |
5 | 5 |
|
6 | 6 | ## Dualstack -- utilities to ease migration to ipv6 |
| 7 | +We recognize there are still barriers to ipv6 adoption. This project aims to identify anti-patterns blocking ipv6 / dual stack |
| 8 | +compatibility like `net.Listen("tcp", "127.0.0.1")` . First , ipv6 |
| 9 | +linter/analyzer identifies faulty code. dualstack offers a few ipv6-compatible |
| 10 | +approaches to secure those interfaces: multilistener to listen to multiple interfaces with a single Accept(), middleware |
| 11 | +to block remote http traffic, and firewall to block remote tcp connections. |
| 12 | + |
| 13 | +Now that the utilties are mature, the next step is to expand the lint suite and |
| 14 | +enter PRs on open source projects to help improve ipv6 compatibility |
| 15 | + |
| 16 | +## How to Listen Properly to support ipv6 and ipv4 dual stack |
| 17 | + |
| 18 | +* `net.Listen("tcp", ":" + port)` is the preferred dual stack listener on all interfaces. The kernel will handle ipv4 & ipv6 |
| 19 | +connections. But for loopback services like oauth, this risks exposure to the internet |
| 20 | + |
| 21 | +### To protect a net.Listener, you can wrap with `middleware.FirewallListener` |
| 22 | +e.g. |
| 23 | +``` |
| 24 | +l, _ := net.Listen("tcp", ":" + port) |
| 25 | +protectedListener := middleware.FirewallListener{l} |
| 26 | +// |
| 27 | +for{ |
| 28 | + // use as usual |
| 29 | + conn, err := protectedListener.Accept() |
| 30 | + if err == net.errClosed{ |
| 31 | + return |
| 32 | + } else if err != nil{ |
| 33 | + // connection was blocked |
| 34 | + continue |
| 35 | + } |
| 36 | + go handleConnection(conn) |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +### To listen to all localhost interfaces , use multilistener |
| 41 | + |
| 42 | +``` |
| 43 | +ml, err := multilistener.NewLocalLoopback() |
| 44 | +if err != nil{ |
| 45 | + panic(err) |
| 46 | +} |
| 47 | +// http will serve on [::1] and 127.0.0.1 |
| 48 | +http.Serve(ml, nil) |
| 49 | +``` |
| 50 | + |
| 51 | +### How to Protect an Existing http.Server |
| 52 | +`LocalOnlyMiddleware` adds a remote-address filter before your handler. Any remote address will receive 403 / unauthorized. |
7 | 53 |
|
8 | | -### Overview |
| 54 | + |
| 55 | +``` |
| 56 | +func ExampleLocalOnlyMiddleware() { |
| 57 | + nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 58 | + w.WriteHeader(http.StatusOK) |
| 59 | + w.Write([]byte("Allowed")) |
| 60 | + }) |
| 61 | + protectedHandler := LocalOnlyMiddleware(nextHandler) |
| 62 | + // for common apps use http.Handle("/", protectedHandler) |
| 63 | + ts := httptest.NewServer(protectedHandler) |
| 64 | + defer ts.Close() |
| 65 | +
|
| 66 | +``` |
| 67 | + |
| 68 | +## Why Now? |
| 69 | + |
| 70 | +Developers are familiar with ipv4 as a stable and secure approach. Local oauth |
| 71 | +services are mature. But With IPv4 address exhaustion accelerating, ipv6 compatibility |
| 72 | +is more urgent now than it has been in the past. |
| 73 | + |
| 74 | +## Package Overview |
9 | 75 |
|
10 | 76 | * multilistener -- listen on multiple local loopback interfaces with multilistener.NewLocalLoopback() |
11 | 77 | * middleware -- block remote connections on net.Listener and http.Server. see middleware.FirewallListener and middleware.LocalOnlyMiddleware |
@@ -446,4 +512,102 @@ import "github.com/tonymet/dualstack/internal/bad-go-code" |
446 | 512 |
|
447 | 513 |
|
448 | 514 |
|
| 515 | +# testing |
| 516 | + |
| 517 | +```go |
| 518 | +import "github.com/tonymet/dualstack/middleware/testing" |
| 519 | +``` |
| 520 | + |
| 521 | +middleware/testing package with mock interfaces for testing net.Listener |
| 522 | + |
| 523 | +## Index |
| 524 | + |
| 525 | +- [type MockConn](<#MockConn>) |
| 526 | + - [func \(m \*MockConn\) Close\(\) error](<#MockConn.Close>) |
| 527 | + - [func \(m \*MockConn\) RemoteAddr\(\) net.Addr](<#MockConn.RemoteAddr>) |
| 528 | +- [type MockListener](<#MockListener>) |
| 529 | + - [func NewMockListener\(\) \*MockListener](<#NewMockListener>) |
| 530 | + - [func \(m \*MockListener\) Accept\(\) \(net.Conn, error\)](<#MockListener.Accept>) |
| 531 | + - [func \(m \*MockListener\) Addr\(\) net.Addr](<#MockListener.Addr>) |
| 532 | + - [func \(m \*MockListener\) Close\(\) error](<#MockListener.Close>) |
| 533 | + |
| 534 | + |
| 535 | +<a name="MockConn"></a> |
| 536 | +## type MockConn |
| 537 | + |
| 538 | +MockConn is a fake net.Conn for testing purposes. |
| 539 | + |
| 540 | +```go |
| 541 | +type MockConn struct { |
| 542 | + net.Conn |
| 543 | + // contains filtered or unexported fields |
| 544 | +} |
| 545 | +``` |
| 546 | + |
| 547 | +<a name="MockConn.Close"></a> |
| 548 | +### func \(\*MockConn\) Close |
| 549 | + |
| 550 | +```go |
| 551 | +func (m *MockConn) Close() error |
| 552 | +``` |
| 553 | + |
| 554 | + |
| 555 | + |
| 556 | +<a name="MockConn.RemoteAddr"></a> |
| 557 | +### func \(\*MockConn\) RemoteAddr |
| 558 | + |
| 559 | +```go |
| 560 | +func (m *MockConn) RemoteAddr() net.Addr |
| 561 | +``` |
| 562 | + |
| 563 | + |
| 564 | + |
| 565 | +<a name="MockListener"></a> |
| 566 | +## type MockListener |
| 567 | + |
| 568 | +MockListener is a fake net.Listener for testing purposes. |
| 569 | + |
| 570 | +```go |
| 571 | +type MockListener struct { |
| 572 | + net.Listener |
| 573 | + // contains filtered or unexported fields |
| 574 | +} |
| 575 | +``` |
| 576 | + |
| 577 | +<a name="NewMockListener"></a> |
| 578 | +### func NewMockListener |
| 579 | + |
| 580 | +```go |
| 581 | +func NewMockListener() *MockListener |
| 582 | +``` |
| 583 | + |
| 584 | + |
| 585 | + |
| 586 | +<a name="MockListener.Accept"></a> |
| 587 | +### func \(\*MockListener\) Accept |
| 588 | + |
| 589 | +```go |
| 590 | +func (m *MockListener) Accept() (net.Conn, error) |
| 591 | +``` |
| 592 | + |
| 593 | + |
| 594 | + |
| 595 | +<a name="MockListener.Addr"></a> |
| 596 | +### func \(\*MockListener\) Addr |
| 597 | + |
| 598 | +```go |
| 599 | +func (m *MockListener) Addr() net.Addr |
| 600 | +``` |
| 601 | + |
| 602 | + |
| 603 | + |
| 604 | +<a name="MockListener.Close"></a> |
| 605 | +### func \(\*MockListener\) Close |
| 606 | + |
| 607 | +```go |
| 608 | +func (m *MockListener) Close() error |
| 609 | +``` |
| 610 | + |
| 611 | + |
| 612 | + |
449 | 613 | Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>) |
0 commit comments