diff --git a/WORKSPACE b/WORKSPACE
index 31247169..bc9c2175 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -112,8 +112,8 @@ container_pull(
name = "prodimage-bionic",
registry = "registry.k0.hswaw.net",
repository = "q3k/prodimage",
- tag = "20190814-1915",
- digest = "sha256:708fdaa7beb76dd2c9d2f3773a202d0f59309a79b59ec40753a729e4e44af588",
+ tag = "20190822-1227",
+ digest = "sha256:1cd1f84169b8e1414a5d511b42909f2d540831c67b6799ae9af8cd6a80d75b5f",
)
container_pull(
diff --git a/bgpwtf/cccampix/birdie/BUILD.bazel b/bgpwtf/cccampix/birdie/BUILD.bazel
new file mode 100644
index 00000000..67fe4805
--- /dev/null
+++ b/bgpwtf/cccampix/birdie/BUILD.bazel
@@ -0,0 +1,21 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
+
+go_library(
+ name = "go_default_library",
+ srcs = ["birdie.go"],
+ importpath = "code.hackerspace.pl/hscloud/bgpwtf/cccampix/birdie",
+ visibility = ["//visibility:private"],
+ deps = [
+ "//bgpwtf/cccampix/proto:go_default_library",
+ "@com_github_golang_glog//:go_default_library",
+ "@org_golang_google_grpc//:go_default_library",
+ "@org_golang_google_grpc//credentials:go_default_library",
+ ],
+)
+
+go_binary(
+ name = "birdie",
+ embed = [":go_default_library"],
+ static = "on",
+ visibility = ["//visibility:public"],
+)
diff --git a/bgpwtf/cccampix/birdie/birdie.go b/bgpwtf/cccampix/birdie/birdie.go
new file mode 100644
index 00000000..f4558448
--- /dev/null
+++ b/bgpwtf/cccampix/birdie/birdie.go
@@ -0,0 +1,197 @@
+package main
+
+import (
+ "context"
+ "crypto/x509"
+ "flag"
+ "fmt"
+ "sort"
+ "strings"
+
+ pb "code.hackerspace.pl/hscloud/bgpwtf/cccampix/proto"
+ "github.com/golang/glog"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials"
+)
+
+var (
+ flagVerifier string
+ flagIdentity string
+ flagRouterID string
+ flagV6 bool
+ flagLocalIP string
+)
+
+func init() {
+ flag.Set("logtostderr", "true")
+}
+
+func main() {
+ flag.StringVar(&flagVerifier, "verifier", "", "Verifier endpoint")
+ flag.StringVar(&flagIdentity, "identity", "", "Router identity")
+ flag.StringVar(&flagRouterID, "router_id", "", "Router ID")
+ flag.StringVar(&flagLocalIP, "local_ip", "", "Local IP")
+ flag.BoolVar(&flagV6, "v6", false, "V6")
+
+ flag.Parse()
+
+ if flagVerifier == "" {
+ glog.Exit("verifier must be set")
+ }
+ if flagIdentity == "" {
+ glog.Exit("identity must be set")
+ }
+ if flagRouterID == "" {
+ glog.Exit("router_id must be set")
+ }
+
+ cpool, _ := x509.SystemCertPool()
+ creds := credentials.NewClientTLSFromCert(cpool, "")
+ conn, err := grpc.Dial(flagVerifier, grpc.WithTransportCredentials(creds))
+ if err != nil {
+ glog.Exitf("Dial: %v", err)
+ }
+
+ ctx := context.Background()
+ verifier := pb.NewVerifierClient(conn)
+
+ req := &pb.RouterHeartbeatRequest{
+ Name: flagIdentity,
+ }
+
+ res, err := verifier.RouterHeartbeat(ctx, req)
+ if err != nil {
+ glog.Exitf("RouterHeartbeat: %v", err)
+ }
+
+ var config string
+ if flagV6 {
+ config = `
+log syslog all;
+
+router id %s;
+debug protocols { states, interfaces, events };
+
+timeformat base iso long;
+timeformat log iso long;
+timeformat protocol iso long;
+timeformat route iso long;
+
+protocol device {}
+
+function net_martians() {
+ return net ~ [ fc00::/7+, fec0::/10+, ::/128-, ::/0{0,15}, ::/0{49,128} ];
+}
+
+function generic_in() {
+ if net_martians() then return false;
+ if bgp_path.len > 64 then return false;
+ if net.len > 64 then return false;
+ if net.len < 12 then return false;
+ return true;
+}
+`
+ config = fmt.Sprintf(config, flagRouterID)
+ } else {
+ config = `
+log syslog all;
+
+router id %s;
+debug protocols { states, interfaces, events };
+
+timeformat base iso long;
+timeformat log iso long;
+timeformat protocol iso long;
+timeformat route iso long;
+
+protocol device {}
+
+function net_martians() {
+ return net ~ [ 169.254.0.0/16+, 172.16.0.0/12+, 192.168.0.0/16+, 10.0.0.0/8+,
+ 127.0.0.0/8+, 224.0.0.0/4+, 240.0.0.0/4+, 0.0.0.0/32-, 0.0.0.0/0{25,32}, 0.0.0.0/0{0,7} ];
+}
+
+function generic_in() {
+ if net_martians() then return false;
+ if bgp_path.len > 64 then return false;
+ if net.len > 24 then return false;
+ if net.len < 8 then return false;
+ return true;
+}
+`
+ config = fmt.Sprintf(config, flagRouterID)
+ }
+
+ sort.Slice(res.AsConfigs, func(i, j int) bool {
+ return res.AsConfigs[i].Asn < res.AsConfigs[j].Asn
+ })
+
+ for _, asc := range res.AsConfigs {
+ sort.Slice(asc.Routers, func(i, j int) bool {
+ return asc.Routers[i].Password < asc.Routers[j].Password
+ })
+ for i, router := range asc.Routers {
+ addr := ""
+ if flagV6 {
+ addr = router.Ipv6
+ } else {
+ addr = router.Ipv4
+ }
+ if addr == "" {
+ continue
+ }
+ peerid := fmt.Sprintf("PEER_%d_%d", asc.Asn, i)
+ prefixes := []string{}
+ for _, prefix := range asc.Prefixes {
+ if flagV6 && !strings.Contains(prefix.Prefix, ":") {
+ continue
+ }
+ if !flagV6 && !strings.Contains(prefix.Prefix, ".") {
+ continue
+ }
+ parts := strings.Split(prefix.Prefix, "/")
+ addr := parts[0]
+ bits := parts[1]
+ filter := fmt.Sprintf("%s/%s{%s,%d}", addr, bits, bits, prefix.MaxLength)
+ if fmt.Sprintf("%d", prefix.MaxLength) == bits {
+ filter = fmt.Sprintf("%s/%s", addr, bits)
+ }
+ prefixes = append(prefixes, filter)
+ }
+ if len(prefixes) == 0 {
+ continue
+ }
+ allowed := strings.Join(prefixes, ",")
+
+ part := `
+filter %s_in {
+ if !generic_in() then reject;
+ if net ~ [ %s ] then accept;
+ reject;
+}
+`
+ part = fmt.Sprintf(part, peerid, allowed)
+ config += part
+
+ part = `
+filter %s_out {
+ accept;
+}
+`
+ part = fmt.Sprintf(part, peerid)
+ config += part
+
+ part = `
+protocol bgp %s {
+ local %s as 208521;
+ neighbor %s as %d;
+ import filter %s_in;
+}
+`
+ part = fmt.Sprintf(part, peerid, flagLocalIP, addr, asc.Asn, peerid)
+ config += part
+ }
+ }
+
+ fmt.Println(config)
+}
diff --git a/bgpwtf/cccampix/frontend/frontend.py b/bgpwtf/cccampix/frontend/frontend.py
index 2a6f240e..c7558a76 100644
--- a/bgpwtf/cccampix/frontend/frontend.py
+++ b/bgpwtf/cccampix/frontend/frontend.py
@@ -17,6 +17,7 @@ logger = logging.getLogger(__name__)
check_info = {
'irr': ('IRR', 'Required IRR entires are present for this AS'),
+ 'pgp': ('PGP', 'The PGP key defined in the IRR entry exists on PGP keyservers'),
}
@@ -55,7 +56,22 @@ def create_app(config=None):
return 'Internal server error.'
return render_template('asn.html', details=details, asn=asn, check_info=check_info)
-
+
+ @app.route('/asn/
+ To get configuration data for your routers, please decode the following GPG secret: + +
curl https://ix-status.bgp.wtf/asn/{{ asn }}/config.gpg | gpg --decrypt+