├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── agent ├── cache │ └── cache.go ├── enforcer │ ├── base.go │ ├── enforcer.go │ ├── enforcer_test.go │ ├── helpers.go │ ├── helpers_test.go │ ├── internal │ │ └── gen │ │ │ └── main.go │ ├── ipsettools.go │ ├── metrics.go │ └── testdata │ │ ├── any-source.iptables │ │ ├── any-source.json │ │ ├── demo-policy.iptables │ │ ├── demo-policy.json │ │ ├── foreing-source-tenant.iptables │ │ ├── foreing-source-tenant.json │ │ ├── no-rules.iptables │ │ ├── no-rules.json │ │ ├── no-source-segment.iptables │ │ ├── no-source-segment.json │ │ ├── no-target-segment.iptables │ │ └── no-target-segment.json ├── exec │ ├── doc.go │ ├── exec.go │ └── fake.go ├── firewall │ ├── common.go │ ├── defs.go │ ├── iptables.go │ ├── iptables_test.go │ ├── iptsave.go │ ├── iptsave_test.go │ ├── mocks_test.go │ └── store.go ├── hosts.go ├── interface.go ├── iptsave │ ├── iptables.go │ ├── iptablesSave │ │ └── main.go │ ├── iptables_test.go │ ├── lexer.go │ └── token.go ├── metrics.go ├── policycache │ └── policy_storage.go ├── policycontroller │ └── controller.go ├── policyhasher │ ├── hasher.go │ ├── hasher_test.go │ └── types.go ├── romanavip.go ├── routes.go ├── routes_test.go ├── rtable │ ├── route_table.go │ ├── route_table_test.go │ └── testdata │ │ ├── rt_tables.case1 │ │ └── rt_tables.golden └── sysctl │ ├── sysctl.go │ ├── sysctl_test.go │ └── testdata │ ├── sysctl.golden │ └── sysctl.zero ├── cli ├── README.md ├── adaptor │ └── client.go ├── commands │ ├── block.go │ ├── docs.go │ ├── host.go │ ├── network.go │ ├── policy.go │ ├── root.go │ └── topology.go ├── kubernetes │ └── tenant.go ├── openstack │ └── tenant.go ├── romana.sample.yaml └── util │ ├── error.go │ └── helpers.go ├── cmd ├── romana │ └── main.go ├── romana_agent │ └── main.go ├── romana_aws │ └── main.go ├── romana_cni │ └── main.go ├── romana_doc │ └── main.go ├── romana_listener │ └── main.go ├── romana_route_publisher │ ├── examples │ │ ├── endpoint-networks.template │ │ └── host-groups.template │ ├── main.go │ └── routes.go └── romanad │ └── main.go ├── cni ├── cni.go ├── cnilink.go ├── divert_rules.go ├── kubernetes.go ├── netconfig.go ├── plugin.go ├── plugin_test.go └── policy.go ├── common ├── api │ ├── errors │ │ ├── errors.go │ │ └── helpers.go │ ├── ipam.go │ ├── policy.go │ ├── policy_test.go │ └── romanavip.go ├── auth.go ├── buildinfo.go ├── client │ ├── client.go │ ├── client_test.go │ ├── idring │ │ ├── idring.go │ │ ├── idring_test.go │ │ ├── merge.go │ │ └── merge_test.go │ ├── ipam.go │ ├── ipam_test.go │ ├── store.go │ └── testdata │ │ ├── Test32_1.json │ │ ├── Test32_2.json │ │ ├── TestBlackout.json │ │ ├── TestBlockReuseMask30.json │ │ ├── TestBlockReuseMask32.json │ │ ├── TestHostAdd713.json │ │ ├── TestHostAdditionSimple.json │ │ ├── TestHostAdditionTags.json │ │ ├── TestHostAllocation.json │ │ ├── TestIPAM_DeallocateIP.json │ │ ├── TestIPReuse.json │ │ ├── TestLabelUpdate.json │ │ ├── TestListBlocks.json │ │ ├── TestMultiNetAllocate.json │ │ ├── TestNodeAssignment.json │ │ ├── TestOutOfBoundsError.json │ │ ├── TestOverlappingCIDRs.json │ │ ├── TestPanic.json │ │ ├── TestParseMultiHostGroupsWithPrefix.json │ │ ├── TestParsePrefixPerHostA.json │ │ ├── TestParsePrefixPerHostB.json │ │ ├── TestParseSimpleFlatNetworkA.json │ │ ├── TestParseSimpleFlatNetworkB.json │ │ ├── TestParseSimpleFlatNetworkC.json │ │ ├── TestParseVPCRoutingForTwoAZs.json │ │ ├── TestPredefinedHosts.json │ │ ├── TestPrefixGenForEmptyGroups.json │ │ ├── TestRepeatedNetwork.json │ │ ├── TestSegments.json │ │ ├── TestTenants.json │ │ ├── TestTenantsBug701.json │ │ ├── TestTopologyGetInputKubeadm.json │ │ ├── TestTopologyGetInputMultiNetworks.json │ │ ├── TestTopologyGetOutputKubeadm.json │ │ ├── TestTopologyGetOutputMultiNetworks.json │ │ ├── TestUpdateTopology.json │ │ └── TestUpdateTopologyInvalidBlockMask.json ├── common_test.go ├── config.go ├── defs.go ├── doc.go ├── errors.go ├── helpers.go ├── log │ └── trace │ │ └── trace.go ├── middleware.go ├── rest_test.go ├── service.go ├── structures.go └── testdata │ ├── agent_proxy_add_policy.sh │ ├── agent_proxy_delete_policy.sh │ ├── demo.rsa │ ├── demo.rsa.pkcs8 │ ├── demo.rsa.pub │ ├── demo.rsa.pub.pkcs8 │ ├── romana.auth.yaml │ ├── romana.hooks.yaml │ ├── romana.sample.yaml │ └── romanad.yaml ├── doc ├── ipam │ └── ipam.yaml ├── policy-examples │ ├── example-policy-dhcp-host.json │ ├── example-policy-dhcp-vm.json │ ├── example-policy-icmp-vm.json │ ├── example-policy-ssh-vm.json │ └── policy-service-agent.json ├── policy.md ├── policy │ └── policy.yaml ├── root │ └── root.yaml ├── security.md ├── tenant │ └── tenant.yaml ├── tools │ ├── code.go │ └── swagger.go └── topology │ └── topology.yaml ├── listener ├── listener.go ├── nodes.go ├── processor.go ├── resources.go ├── resources_test.go ├── romanavip.go ├── testdata │ ├── any-source.json │ ├── any-source.kube │ ├── any-source.yml │ ├── demo-policy.json │ ├── demo-policy.kube │ ├── foreing-source-tenant.json │ ├── foreing-source-tenant.kube │ ├── foreing-source-tenant.yml │ ├── no-rules.json │ ├── no-rules.kube │ ├── no-rules.yml │ ├── no-source-segment.json │ ├── no-source-segment.kube │ ├── no-source-segment.yml │ ├── no-target-segment.json │ ├── no-target-segment.kube │ └── no-target-segment.yml ├── translate.go └── translate_test.go ├── pkg └── policytools │ ├── blueprint.go │ ├── blueprint_table.go │ ├── data │ └── policy.tsv │ ├── doc.go │ ├── internal │ └── gen │ │ └── main.go │ ├── iterator.go │ ├── iterator_test.go │ ├── templates │ └── blueprint.go_template │ └── validate.go ├── routepublisher ├── bird │ └── bird.go └── publisher │ └── publisher.go ├── server ├── handlers.go ├── policy_test.go └── romanad.go └── test ├── cmd └── fuzzer │ └── main.go ├── doc.go ├── integration_chunk_test.go └── integration_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | *.sqlite3 6 | 7 | # Folders 8 | _obj 9 | _test 10 | .idea 11 | 12 | # Architecture specific extensions/prefixes 13 | *.[568vq] 14 | [568vq].out 15 | 16 | *.cgo1.go 17 | *.cgo2.c 18 | _cgo_defun.c 19 | _cgo_gotypes.go 20 | _cgo_export.* 21 | 22 | _testmain.go 23 | 24 | *.exe 25 | *.test 26 | *.prof 27 | 28 | tags 29 | 30 | # Emacs artifacts 31 | *~ 32 | \#*\# 33 | .\#*\# 34 | 35 | # Eclipse artifacts 36 | .project 37 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # test: run unit tests with coverage turned on. 3 | # vet: run go vet for catching subtle errors. 4 | # lint: run golint. 5 | # 6 | 7 | services = $$GOPATH/bin/romanad\ 8 | $$GOPATH/bin/romana\ 9 | $$GOPATH/bin/romana_agent\ 10 | $$GOPATH/bin/romana_cni\ 11 | $$GOPATH/bin/romana_aws\ 12 | $$GOPATH/bin/romana_listener\ 13 | $$GOPATH/bin/romana_route_publisher\ 14 | $$GOPATH/bin/romana_doc 15 | 16 | UPX_VERSION := $(shell upx --version 2>/dev/null) 17 | 18 | all: fmt vet install 19 | 20 | buildbranch := $(shell git describe --all --abbrev=7 --always) 21 | buildcommit := $(shell git log --pretty=format:"%h" | head -n 1) 22 | buildinfo := ${buildbranch}-${buildcommit} 23 | install: 24 | go list -f '{{.ImportPath}}' "./..." | \ 25 | grep -v /vendor/ | xargs go install -race -ldflags \ 26 | "-X github.com/romana/core/common.buildInfo=$(buildinfo) \ 27 | -X github.com/romana/core/common.buildTimeStamp=`date -u '+%Y-%m-%d_%I:%M:%S%p'`" 28 | 29 | test: 30 | go list -f '{{.ImportPath}}' "./..." | \ 31 | grep -v /vendor/ | xargs go test -timeout=30s -cover 32 | 33 | testv: 34 | go list -f '{{.ImportPath}}' "./..." | \ 35 | grep -v /vendor/ | xargs go test -v -timeout=30s -cover 36 | 37 | vet: 38 | go list -f '{{.ImportPath}}' "./..." | \ 39 | grep -v /vendor/ | xargs go vet 40 | 41 | fmt: 42 | go list -f '{{.ImportPath}}' "./..." | \ 43 | grep -v /vendor/ | xargs go fmt 44 | 45 | lint: 46 | go list -f '{{.Dir}}' "./..." | \ 47 | grep -v /vendor/ | xargs -n 1 golint 48 | 49 | upx: 50 | ifndef UPX_VERSION 51 | $(error "No upx in $(PATH), consider doing apt-get install upx") 52 | endif 53 | upx $(services) 54 | 55 | clean: 56 | rm $(services) 57 | 58 | doc_main_file = $(shell go list -f '{{.Dir}}' "./..." |fgrep github.com/romana/core/tools/doc)/doc.go 59 | root_dir = $(shell go list -f '{{.Root}}' "./..." | head -1) 60 | swagger_dir_tmp = $(shell mktemp -d) 61 | index=$(shell for svc in agent ipam root tenant topology policy; do echo '
  • '$$svc'
  • '; echo; done) 62 | 63 | swagger: 64 | cd $(swagger_dir_tmp) 65 | echo In `pwd` 66 | go run $(doc_main_file) $(root_dir) > swagger.out 2>&1 67 | # If the above succeeded, we do not need to keep the output, 68 | # so remove it. 69 | rm swagger.out 70 | echo '

    Romana services

    ' > doc/index.html 71 | echo '' >> doc/index.html 74 | echo '' >> doc/index.html 75 | cat index.html 76 | aws s3 sync --acl public-read doc s3://swagger.romana.io/romana 77 | echo Latest doc uploaded to http://swagger.romana.io.s3-website-us-west-1.amazonaws.com/romana/index.html 78 | rm -rf $(swagger_dir_tmp) 79 | 80 | .PHONY: test vet lint all install clean fmt upx testv 81 | -------------------------------------------------------------------------------- /agent/cache/cache.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package cache 17 | 18 | import "sync" 19 | 20 | type Interface interface { 21 | Put(string, interface{}) 22 | Get(string) (interface{}, bool) 23 | Delete(string) 24 | List() []interface{} 25 | Keys() []string 26 | } 27 | 28 | func New() Interface { 29 | items := make(map[string]interface{}) 30 | m := &sync.Mutex{} 31 | return Cache{Items: items, Mutex: m} 32 | } 33 | 34 | // note: implementation here uses methods on struct 35 | // instead of pointers because map itself is a pointer. 36 | 37 | type Cache struct { 38 | Items map[string]interface{} 39 | *sync.Mutex 40 | } 41 | 42 | func (s Cache) Put(key string, item interface{}) { 43 | s.Lock() 44 | defer s.Unlock() 45 | s.Items[key] = item 46 | } 47 | 48 | func (s Cache) Get(key string) (interface{}, bool) { 49 | s.Lock() 50 | defer s.Unlock() 51 | item, ok := s.Items[key] 52 | return item, ok 53 | } 54 | 55 | func (s Cache) Delete(key string) { 56 | s.Lock() 57 | defer s.Unlock() 58 | delete(s.Items, key) 59 | } 60 | 61 | func (s Cache) List() []interface{} { 62 | s.Lock() 63 | defer s.Unlock() 64 | var result []interface{} 65 | 66 | for _, v := range s.Items { 67 | result = append(result, v) 68 | } 69 | 70 | return result 71 | } 72 | 73 | func (s Cache) Keys() []string { 74 | s.Lock() 75 | defer s.Unlock() 76 | var result []string 77 | 78 | for k, _ := range s.Items { 79 | result = append(result, k) 80 | } 81 | 82 | return result 83 | } 84 | -------------------------------------------------------------------------------- /agent/enforcer/helpers_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // +build ignore 17 | 18 | package enforcer 19 | 20 | import ( 21 | "net" 22 | "testing" 23 | 24 | "github.com/romana/core/agent/iptsave" 25 | "github.com/romana/core/common" 26 | ) 27 | 28 | type MockNC struct { 29 | netBits uint 30 | portBits uint 31 | tenantBits uint 32 | segmentBits uint 33 | endpointBits uint 34 | } 35 | 36 | func (m MockNC) PrefixBits() uint { 37 | return m.netBits 38 | } 39 | 40 | func (m MockNC) PortBits() uint { 41 | return m.portBits 42 | } 43 | 44 | func (m MockNC) TenantBits() uint { 45 | return m.tenantBits 46 | } 47 | 48 | func (m MockNC) SegmentBits() uint { 49 | return m.segmentBits 50 | } 51 | 52 | func (m MockNC) EndpointBits() uint { 53 | return m.endpointBits 54 | } 55 | 56 | func (m MockNC) EndpointNetmaskSize() uint64 { 57 | return uint64(0) 58 | } 59 | 60 | func (m MockNC) RomanaGW() net.IP { 61 | return net.ParseIP("10.0.0.1") 62 | } 63 | 64 | func (m MockNC) PNetCIDR() (cidr *net.IPNet, err error) { 65 | return &net.IPNet{}, nil 66 | } 67 | 68 | func TestMakeIngressTenantJumpRule(t *testing.T) { 69 | netConfig := MockNC{uint(8), uint(8), uint(4), uint(4), uint(8)} 70 | rule := MakeIngressTenantJumpRule(common.Tenant{NetworkID: uint64(2)}, netConfig) 71 | t.Log(rule) 72 | } 73 | 74 | func makeMockRule(body string, target string) *iptsave.IPrule { 75 | return &iptsave.IPrule{ 76 | Match: []*iptsave.Match{ 77 | &iptsave.Match{ 78 | Body: body, 79 | }, 80 | }, 81 | Action: iptsave.IPtablesAction{ 82 | Type: iptsave.ActionDefault, 83 | Body: target, 84 | }, 85 | } 86 | } 87 | 88 | func TestInsertNormalRule(t *testing.T) { 89 | fakeChain := &iptsave.IPchain{ 90 | Name: "Test", 91 | Rules: []*iptsave.IPrule{ 92 | makeMockRule("Header Rule 1", "ACCEPT"), 93 | makeMockRule("Header Rule 2", "ACCEPT"), 94 | makeMockRule("Footer Rule 1", "RETURN"), 95 | makeMockRule("Footer Rule 2", "DROP"), 96 | }, 97 | } 98 | 99 | InsertNormalRule(fakeChain, makeMockRule("Normal Rule 1", "LOG")) 100 | if fakeChain.Rules[2].Action.Body != "LOG" { 101 | t.Errorf("Unexpected rule at first normal position, expect LOG got %s", fakeChain.Rules[2]) 102 | } 103 | t.Logf("%s", fakeChain) 104 | 105 | fakeChain = &iptsave.IPchain{ 106 | Name: "Test", 107 | Rules: []*iptsave.IPrule{ 108 | makeMockRule("Footer Rule 1", "RETURN"), 109 | makeMockRule("Footer Rule 2", "DROP"), 110 | }, 111 | } 112 | 113 | InsertNormalRule(fakeChain, makeMockRule("Normal Rule 1", "LOG")) 114 | if fakeChain.Rules[0].Action.Body != "LOG" { 115 | t.Errorf("Unexpected rule at first normal position, expect LOG got %s", fakeChain.Rules[0]) 116 | } 117 | t.Logf("%s", fakeChain) 118 | 119 | fakeChain = &iptsave.IPchain{ 120 | Name: "Test", 121 | Rules: []*iptsave.IPrule{ 122 | makeMockRule("Header Rule 1", "ACCEPT"), 123 | makeMockRule("Header Rule 2", "ACCEPT"), 124 | }, 125 | } 126 | 127 | InsertNormalRule(fakeChain, makeMockRule("Normal Rule 1", "LOG")) 128 | if fakeChain.Rules[2].Action.Body != "LOG" { 129 | t.Errorf("Unexpected rule at first normal position, expect LOG got %s", fakeChain.Rules[2]) 130 | } 131 | t.Logf("%s", fakeChain) 132 | 133 | } 134 | -------------------------------------------------------------------------------- /agent/enforcer/internal/gen/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // +build ignore 17 | 18 | package main 19 | 20 | import ( 21 | "encoding/csv" 22 | "flag" 23 | "os" 24 | "text/template" 25 | ) 26 | 27 | func main() { 28 | templatePath := flag.String("template", "", "template to render") 29 | dataFilePath := flag.String("data", "", "") 30 | outputPath := flag.String("out", "", "") 31 | flag.Parse() 32 | 33 | template, err := template.ParseFiles(*templatePath) 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | data, err := os.Open(*dataFilePath) 39 | if err != nil { 40 | panic(err) 41 | } 42 | 43 | r := csv.NewReader(data) 44 | r.Comma = '\t' 45 | r.LazyQuotes = true 46 | 47 | records, err := r.ReadAll() 48 | if err != nil { 49 | panic(err) 50 | } 51 | 52 | out, err := os.OpenFile(*outputPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) 53 | if err != nil { 54 | panic(err) 55 | } 56 | defer out.Close() 57 | 58 | err = template.Execute(out, records) 59 | if err != nil { 60 | panic(err) 61 | } 62 | 63 | err = out.Close() 64 | if err != nil { 65 | panic(err) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /agent/enforcer/ipsettools.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // Policy enforcer package translates romana policies into iptables rules. 17 | package enforcer 18 | 19 | import ( 20 | "context" 21 | "time" 22 | 23 | "github.com/romana/ipset" 24 | ) 25 | 26 | // updateIpsets is a flush/rebuild implementation of ipset update 27 | // it will wipe all sets in a system and populate new ones. 28 | // TODO for Stas this monopolizes ipsets 29 | // and also pollutes with sets which never deleted. 30 | // Need cleaner implementation 31 | func updateIpsets(ctx context.Context, sets *ipset.Ipset) error { 32 | 33 | err := attemptIpsetCleanup(ctx, sets) 34 | if err != nil { 35 | return err 36 | } 37 | 38 | ipsetHandle, err := ipset.NewHandle() 39 | if err != nil { 40 | return err 41 | } 42 | 43 | err = ipsetHandle.Start() 44 | if err != nil { 45 | return err 46 | } 47 | 48 | err = ipsetHandle.Create(sets) 49 | if err != nil { 50 | return err 51 | } 52 | 53 | err = ipsetHandle.Add(sets) 54 | if err != nil { 55 | return err 56 | } 57 | 58 | err = ipsetHandle.Quit() 59 | if err != nil { 60 | return err 61 | } 62 | 63 | cTimout, cancel := context.WithTimeout(ctx, 10*time.Second) 64 | defer cancel() 65 | err = ipsetHandle.Wait(cTimout) 66 | if err != nil { 67 | return err 68 | } 69 | 70 | return nil 71 | 72 | } 73 | 74 | // attemptIpsetCleanup attempts to destroy every set. 75 | // TODO make it less nuclear. 76 | func attemptIpsetCleanup(ctx context.Context, sets *ipset.Ipset) error { 77 | iset, _ := ipset.Load(ctx) 78 | for _, set := range iset.Sets { 79 | _, _ = ipset.Destroy(set) 80 | } 81 | 82 | // flush everything that survived mass destroy. 83 | _, err := ipset.Flush(nil) 84 | 85 | return err 86 | } 87 | -------------------------------------------------------------------------------- /agent/enforcer/metrics.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package enforcer 17 | 18 | import ( 19 | "fmt" 20 | 21 | "github.com/prometheus/client_golang/prometheus" 22 | ) 23 | 24 | var ( 25 | ErrMakeSets = prometheus.NewCounter( 26 | prometheus.CounterOpts{ 27 | Name: "romana_err_make_sets_total", 28 | Help: "Number of errors attempting to build ipset Sets.", 29 | }, 30 | ) 31 | ErrApplySets = prometheus.NewCounter( 32 | prometheus.CounterOpts{ 33 | Name: "romana_err_apply_sets_total", 34 | Help: "Number of errors attempting to apply ipset Sets.", 35 | }, 36 | ) 37 | ErrValidateIptables = prometheus.NewCounter( 38 | prometheus.CounterOpts{ 39 | Name: "romana_err_validate_iptables_total", 40 | Help: "Number of errors when validating iptables.", 41 | }, 42 | ) 43 | ErrApplyIptables = prometheus.NewCounter( 44 | prometheus.CounterOpts{ 45 | Name: "romana_err_apply_iptables_total", 46 | Help: "Number of errors attempting to apply iptables.", 47 | }, 48 | ) 49 | NumPolicyUpdates = prometheus.NewCounter( 50 | prometheus.CounterOpts{ 51 | Name: "romana_policy_updates_total", 52 | Help: "Number of policy updates processed.", 53 | }, 54 | ) 55 | NumBlockUpdates = prometheus.NewCounter( 56 | prometheus.CounterOpts{ 57 | Name: "romana_block_updates_total", 58 | Help: "Number of block updates processed.", 59 | }, 60 | ) 61 | NumEnforcerTick = prometheus.NewCounter( 62 | prometheus.CounterOpts{ 63 | Name: "romana_enforcer_ticks_total", 64 | Help: "Number of enforcer ticks since start.", 65 | }, 66 | ) 67 | NumManagedSets = prometheus.NewGauge( 68 | prometheus.GaugeOpts{ 69 | Name: "romana_managed_sets", 70 | Help: "Number ipset sets managed by Romana policy.", 71 | }, 72 | ) 73 | NumPolicyRules = prometheus.NewGauge( 74 | prometheus.GaugeOpts{ 75 | Name: "romana_policy_rules", 76 | Help: "Number of Romana policy rules applied to the host.", 77 | }, 78 | ) 79 | ) 80 | 81 | // MetricsRegister registers package global metrics into registry provided, 82 | // for later exposure. 83 | func MetricsRegister(registry *prometheus.Registry) error { 84 | if registry == nil { 85 | return fmt.Errorf("registry must not be nil") 86 | } 87 | 88 | for _, counter := range []prometheus.Counter{ 89 | ErrMakeSets, 90 | ErrApplySets, 91 | ErrValidateIptables, 92 | ErrApplyIptables, 93 | NumPolicyUpdates, 94 | NumBlockUpdates, 95 | NumEnforcerTick, 96 | NumManagedSets, 97 | NumPolicyRules, 98 | } { 99 | err := registry.Register(counter) 100 | if err != nil { 101 | return err 102 | } 103 | } 104 | 105 | return nil 106 | } 107 | -------------------------------------------------------------------------------- /agent/enforcer/testdata/any-source.iptables: -------------------------------------------------------------------------------- 1 | *filter 2 | :ROMANA-FORWARD-IN - 3 | :ROMANA-P-702122ee51ef3cb5 - 4 | :ROMANA-P-702122ee51ef3cb5_X - 5 | :ROMANA-P-702122ee51ef3cb5_R - 6 | -A ROMANA-FORWARD-IN -j ROMANA-P-702122ee51ef3cb5 7 | -A ROMANA-P-702122ee51ef3cb5 -m set --match-set ROMANA-446f0022c0a89d93 dst -j ROMANA-P-702122ee51ef3cb5_X 8 | -A ROMANA-P-702122ee51ef3cb5_X -j ROMANA-P-702122ee51ef3cb5_R 9 | -A ROMANA-P-702122ee51ef3cb5_R -p tcp --dport 80 -j ACCEPT 10 | COMMIT 11 | -------------------------------------------------------------------------------- /agent/enforcer/testdata/any-source.json: -------------------------------------------------------------------------------- 1 | {"id":"kube.tenant-a.pol1.d3122af3-a4cc-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"peer":"any"}],"rules":[{"protocol":"tcp","ports":[80]}]}]} 2 | -------------------------------------------------------------------------------- /agent/enforcer/testdata/demo-policy.iptables: -------------------------------------------------------------------------------- 1 | *filter 2 | :ROMANA-FORWARD-IN - 3 | :ROMANA-P-aca884e8b43cc0e7 - 4 | :ROMANA-P-aca884e8b43cc0e7_X - 5 | :ROMANA-P-aca884e8b43cc0e7_R - 6 | -A ROMANA-FORWARD-IN -j ROMANA-P-aca884e8b43cc0e7 7 | -A ROMANA-P-aca884e8b43cc0e7 -m set --match-set ROMANA-446f0022c0a89d93 dst -j ROMANA-P-aca884e8b43cc0e7_X 8 | -A ROMANA-P-aca884e8b43cc0e7_X -m set --match-set ROMANA-78c82e6c585c36a6 src -j ROMANA-P-aca884e8b43cc0e7_R 9 | -A ROMANA-P-aca884e8b43cc0e7_R -p tcp --dport 80 -j ACCEPT 10 | COMMIT 11 | -------------------------------------------------------------------------------- /agent/enforcer/testdata/demo-policy.json: -------------------------------------------------------------------------------- 1 | {"id":"kube.tenant-a.pol1.a8e9618f-ab1d-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"tenant_id":"tenant-a","segment_id":"frontend"}],"rules":[{"protocol":"tcp","ports":[80]}]}]} 2 | -------------------------------------------------------------------------------- /agent/enforcer/testdata/foreing-source-tenant.iptables: -------------------------------------------------------------------------------- 1 | *filter 2 | :ROMANA-FORWARD-IN - 3 | :ROMANA-P-b62df31062b6b496 - 4 | :ROMANA-P-b62df31062b6b496_X - 5 | :ROMANA-P-b62df31062b6b496_R - 6 | -A ROMANA-FORWARD-IN -j ROMANA-P-b62df31062b6b496 7 | -A ROMANA-P-b62df31062b6b496 -m set --match-set ROMANA-446f0022c0a89d93 dst -j ROMANA-P-b62df31062b6b496_X 8 | -A ROMANA-P-b62df31062b6b496_X -m set --match-set ROMANA-fe6dda4923beb047 src -j ROMANA-P-b62df31062b6b496_R 9 | -A ROMANA-P-b62df31062b6b496_R -p tcp --dport 80 -j ACCEPT 10 | COMMIT 11 | -------------------------------------------------------------------------------- /agent/enforcer/testdata/foreing-source-tenant.json: -------------------------------------------------------------------------------- 1 | {"id":"kube.tenant-a.pol1.0ef621c5-a4cd-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"tenant_id":"kube-system"}],"rules":[{"protocol":"tcp","ports":[80]}]}]} 2 | -------------------------------------------------------------------------------- /agent/enforcer/testdata/no-rules.iptables: -------------------------------------------------------------------------------- 1 | *filter 2 | :ROMANA-FORWARD-IN - 3 | :ROMANA-P-a70997e12148f097 - 4 | :ROMANA-P-a70997e12148f097_X - 5 | :ROMANA-P-a70997e12148f097_R - 6 | -A ROMANA-FORWARD-IN -j ROMANA-P-a70997e12148f097 7 | -A ROMANA-P-a70997e12148f097 -m set --match-set ROMANA-446f0022c0a89d93 dst -j ROMANA-P-a70997e12148f097_X 8 | -A ROMANA-P-a70997e12148f097_X -m set --match-set ROMANA-78c82e6c585c36a6 src -j ROMANA-P-a70997e12148f097_R 9 | -A ROMANA-P-a70997e12148f097_R -j ACCEPT 10 | COMMIT 11 | -------------------------------------------------------------------------------- /agent/enforcer/testdata/no-rules.json: -------------------------------------------------------------------------------- 1 | {"id":"kube.tenant-a.pol1.4bf9aba4-a4cd-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"tenant_id":"tenant-a","segment_id":"frontend"}],"rules":[{"protocol":"any"}]}]} 2 | -------------------------------------------------------------------------------- /agent/enforcer/testdata/no-source-segment.iptables: -------------------------------------------------------------------------------- 1 | *filter 2 | :ROMANA-FORWARD-IN - 3 | :ROMANA-P-d881c0948d2a25ec - 4 | :ROMANA-P-d881c0948d2a25ec_X - 5 | :ROMANA-P-d881c0948d2a25ec_R - 6 | -A ROMANA-FORWARD-IN -j ROMANA-P-d881c0948d2a25ec 7 | -A ROMANA-P-d881c0948d2a25ec -m set --match-set ROMANA-446f0022c0a89d93 dst -j ROMANA-P-d881c0948d2a25ec_X 8 | -A ROMANA-P-d881c0948d2a25ec_X -m set --match-set ROMANA-bc42e93f0aa666e1 src -j ROMANA-P-d881c0948d2a25ec_R 9 | -A ROMANA-P-d881c0948d2a25ec_R -p tcp --dport 80 -j ACCEPT 10 | COMMIT 11 | -------------------------------------------------------------------------------- /agent/enforcer/testdata/no-source-segment.json: -------------------------------------------------------------------------------- 1 | {"id":"kube.tenant-a.pol1.7bcdb586-a4cd-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"tenant_id":"tenant-a"}],"rules":[{"protocol":"tcp","ports":[80]}]}]} 2 | -------------------------------------------------------------------------------- /agent/enforcer/testdata/no-target-segment.iptables: -------------------------------------------------------------------------------- 1 | *filter 2 | :ROMANA-FORWARD-IN - 3 | :ROMANA-P-1853ba0d91f85673 - 4 | :ROMANA-P-1853ba0d91f85673_X - 5 | :ROMANA-P-1853ba0d91f85673_R - 6 | -A ROMANA-FORWARD-IN -j ROMANA-P-1853ba0d91f85673 7 | -A ROMANA-P-1853ba0d91f85673 -m set --match-set ROMANA-bc42e93f0aa666e1 dst -j ROMANA-P-1853ba0d91f85673_X 8 | -A ROMANA-P-1853ba0d91f85673_X -m set --match-set ROMANA-78c82e6c585c36a6 src -j ROMANA-P-1853ba0d91f85673_R 9 | -A ROMANA-P-1853ba0d91f85673_R -p tcp --dport 80 -j ACCEPT 10 | COMMIT 11 | -------------------------------------------------------------------------------- /agent/enforcer/testdata/no-target-segment.json: -------------------------------------------------------------------------------- 1 | {"id":"kube.tenant-a.pol1.a7cac6f1-a4cd-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a"}],"ingress":[{"peers":[{"tenant_id":"tenant-a","segment_id":"frontend"}],"rules":[{"protocol":"tcp","ports":[80]}]}]} 2 | -------------------------------------------------------------------------------- /agent/exec/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // Package provides wrapper around os.exec for purpose of testing 17 | package exec 18 | -------------------------------------------------------------------------------- /agent/exec/exec.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package exec 17 | 18 | import ( 19 | "io" 20 | "os/exec" 21 | ) 22 | 23 | // Interfaces in this file are designed to provide 24 | // a "mockable" implementation of os/exec for testing purposes. 25 | 26 | // Executable is a facade to exec.Command().Output(). 27 | type Executable interface { 28 | Exec(cmd string, args []string) ([]byte, error) 29 | Cmd(cmd string, args []string) Cmd 30 | } 31 | 32 | // Cmd is a facade to exec.Cmd. 33 | type Cmd interface { 34 | StdinPipe() (io.WriteCloser, error) 35 | CombinedOutput() ([]byte, error) 36 | Start() error 37 | Wait() error 38 | } 39 | 40 | // DefaultExecutor is a default implementation of Executable that passes 41 | // back to standard library. 42 | type DefaultExecutor struct{} 43 | 44 | // Exec implements Executable interface by proxiyng all requests to exec.Command(). 45 | func (DefaultExecutor) Exec(cmd string, args []string) ([]byte, error) { 46 | cmdObj := exec.Command(cmd, args...) 47 | out, err := cmdObj.CombinedOutput() 48 | return out, err 49 | } 50 | 51 | // Cmd implements Executable interface. 52 | func (DefaultExecutor) Cmd(cmd string, args []string) Cmd { 53 | return exec.Command(cmd, args...) 54 | } 55 | -------------------------------------------------------------------------------- /agent/exec/fake.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package exec 17 | 18 | import ( 19 | "fmt" 20 | "io" 21 | "strings" 22 | ) 23 | 24 | // FakeExecutor implements Executable 25 | // stores faked Output, Error and commands recorded by Exec. 26 | type FakeExecutor struct { 27 | Output []byte 28 | Error error 29 | Commands *string 30 | } 31 | 32 | // FakeCmd implement Cmd interface for testing purposes. 33 | type FakeCmd struct{} 34 | 35 | func (FakeCmd) StdinPipe() (io.WriteCloser, error) { 36 | // TODO 37 | return nil, nil 38 | } 39 | 40 | func (FakeCmd) CombinedOutput() ([]byte, error) { 41 | // TODO 42 | var empty []byte 43 | return empty, nil 44 | } 45 | 46 | func (FakeCmd) Start() error { 47 | return nil 48 | } 49 | 50 | func (FakeCmd) Wait() error { 51 | return nil 52 | } 53 | 54 | // Exec is a method of fake executor that will record all incoming commands 55 | // and use faked Output and Error. 56 | func (x *FakeExecutor) Exec(cmd string, args []string) ([]byte, error) { 57 | var c string 58 | if x.Commands == nil { 59 | c = fmt.Sprintf("%s %s", cmd, strings.Join(args, " ")) 60 | } else { 61 | c = fmt.Sprintf("%s\n%s %s", *x.Commands, cmd, strings.Join(args, " ")) 62 | } 63 | x.Commands = &c 64 | return x.Output, x.Error 65 | } 66 | 67 | func (x *FakeExecutor) Cmd(cmd string, args []string) Cmd { 68 | return FakeCmd{} 69 | } 70 | -------------------------------------------------------------------------------- /agent/firewall/iptsave_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | // 16 | // IPtables save/restore based firewall implementations. 17 | 18 | package firewall 19 | 20 | import ( 21 | "bytes" 22 | "net" 23 | "testing" 24 | 25 | "github.com/romana/core/agent/iptsave" 26 | ) 27 | 28 | const ( 29 | defaultRule = ` 30 | # Generated by iptables-save v1.4.21 on Tue Aug 30 12:21:37 2016 31 | *filter 32 | :INPUT ACCEPT [2258:359835] 33 | :FORWARD ACCEPT [0:0] 34 | :OUTPUT ACCEPT [2202:367244] 35 | :DOCKER - [0:0] 36 | :DOCKER-ISOLATION - [0:0] 37 | :KUBE-SERVICES - [0:0] 38 | -A FORWARD -j DOCKER-ISOLATION 39 | -A FORWARD -o docker0 -j DOCKER 40 | -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 41 | -A FORWARD -i docker0 ! -o docker0 -j ACCEPT 42 | -A FORWARD -i docker0 -o docker0 -j ACCEPT 43 | -A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES 44 | -A DOCKER-ISOLATION -j RETURN 45 | COMMIT 46 | ` 47 | 48 | testRule1 = "OTHERCHAIN -p tcp -m tcp --dport 442 ! -m tcp --sport 443 -j ACCEPT" 49 | testRule2 = "MYCHAIN -p tcp -m tcp --dport 443 ! -m tcp --sport 442 -j OTHERCHAIN" 50 | testRule3 = "MYCHAIN -p tcp -m tcp --dport 80 ! -m tcp --sport 71 -j OTHERCHAIN" 51 | ) 52 | 53 | /* 54 | func TestInit(t *testing.T) { 55 | currentRules := iptsave.Parse(bytes.NewReader([]byte(defaultRule))) 56 | } 57 | */ 58 | 59 | func TestSetDefaultRules(t *testing.T) { 60 | rules := []FirewallRule{} 61 | 62 | rules = append(rules, &IPtablesRule{Body: testRule1}) 63 | rules = append(rules, &IPtablesRule{Body: testRule2}) 64 | rules = append(rules, &IPtablesRule{Body: testRule3}) 65 | 66 | firewall := IPTsaveFirewall{DesiredState: &iptsave.IPtables{}} 67 | firewall.DesiredState.Tables = append(firewall.DesiredState.Tables, &iptsave.IPtable{Name: "filter"}) 68 | 69 | err := firewall.SetDefaultRules(rules) 70 | if err != nil { 71 | t.Error(err) 72 | } 73 | 74 | expect := `*filter 75 | :MYCHAIN 76 | :OTHERCHAIN 77 | -A MYCHAIN -p tcp -m tcp --dport 443 ! -m tcp --sport 442 -j OTHERCHAIN 78 | -A MYCHAIN -p tcp -m tcp --dport 80 ! -m tcp --sport 71 -j OTHERCHAIN 79 | -A OTHERCHAIN -p tcp -m tcp --dport 442 ! -m tcp --sport 443 -j ACCEPT 80 | COMMIT` 81 | 82 | if len(firewall.DesiredState.Render()) != 253 { 83 | t.Errorf("expect %s\n got %s\n", firewall.DesiredState.Render(), expect) 84 | } 85 | } 86 | 87 | func TestSetEndpoint(t *testing.T) { 88 | firewall := IPTsaveFirewall{ 89 | DesiredState: &iptsave.IPtables{}, 90 | CurrentState: &iptsave.IPtables{}, 91 | networkConfig: mockNetworkConfig{}, 92 | } 93 | firewall.CurrentState.Parse(bytes.NewReader([]byte(defaultRule))) 94 | firewall.DesiredState.Tables = append(firewall.DesiredState.Tables, &iptsave.IPtable{Name: "filter"}) 95 | 96 | if err := firewall.SetEndpoint(mockFirewallEndpoint{"eth0", "A", net.ParseIP("127.0.0.1")}); err != nil { 97 | t.Errorf("%s", err) 98 | } 99 | 100 | expect := ` 101 | *filter 102 | :INPUT - 103 | :OUTPUT - 104 | :FORWARD - 105 | :ROMANA-INPUT - 106 | :ROMANA-FORWARD-OUT - 107 | :ROMANA-FORWARD-IN - 108 | -A INPUT -i eth -j ROMANA-INPUT 109 | -A OUTPUT -o eth -j ROMANA-FORWARD-IN 110 | -A FORWARD -i eth -j ROMANA-FORWARD-OUT 111 | -A FORWARD -o eth -j ROMANA-FORWARD-IN 112 | COMMIT` 113 | 114 | if len(firewall.DesiredState.Render()) != 263 { 115 | t.Errorf("expect %s\n got %s\n", expect, firewall.DesiredState.Render()) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /agent/firewall/mocks_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | // 16 | // Fake structures used in testing. 17 | 18 | package firewall 19 | 20 | import ( 21 | "database/sql" 22 | _ "github.com/mattn/go-sqlite3" 23 | "net" 24 | "sync" 25 | ) 26 | 27 | // mockNetworkConfig implements NetConfig 28 | type mockNetworkConfig struct{} 29 | 30 | // EndpointNetmaskSize returns integer value (aka size) of endpoint netmask. 31 | func (c mockNetworkConfig) EndpointNetmaskSize() uint64 { 32 | // dc.EndpointSpaceBits = 0 33 | return 32 34 | } 35 | 36 | // PNetCIDR returns pseudo net cidr in net.IPNet format. 37 | func (c mockNetworkConfig) PNetCIDR() (cidr *net.IPNet, err error) { 38 | // dc.Cidr = "10.0.0.0/8" 39 | _, cidr, err = net.ParseCIDR("10.0.0.0/8") 40 | return 41 | } 42 | 43 | // PrefixBits returns tenant bits value from POC config. 44 | func (c mockNetworkConfig) PrefixBits() uint { 45 | return uint(8) 46 | } 47 | 48 | // PortBits returns tenant bits value from POC config. 49 | func (c mockNetworkConfig) PortBits() uint { 50 | return uint(8) 51 | } 52 | 53 | // TenantBits returns tenant bits value from POC config. 54 | func (c mockNetworkConfig) TenantBits() uint { 55 | // dc.TenantBits = 4 56 | return uint(4) 57 | } 58 | 59 | // SegmentBits returns segment bits value from POC config. 60 | func (c mockNetworkConfig) SegmentBits() uint { 61 | // dc.SegmentBits = 4 62 | return uint(4) 63 | } 64 | 65 | // EndpointBits returns endpoint bits value from POC config. 66 | func (c mockNetworkConfig) EndpointBits() uint { 67 | // dc.EndpointBits = 8 68 | return uint(8) 69 | } 70 | 71 | // RomanaGW returns current romana gateway. 72 | func (c mockNetworkConfig) RomanaGW() net.IP { 73 | // {Ip: "172.17.0.1"} 74 | return net.ParseIP("172.17.0.1") 75 | } 76 | 77 | // mockNetworkConfig implements FirewallEndpoint 78 | type mockFirewallEndpoint struct { 79 | Name string 80 | Mac string 81 | IP net.IP 82 | } 83 | 84 | func (c mockFirewallEndpoint) GetName() string { 85 | return c.Name 86 | } 87 | 88 | func (c mockFirewallEndpoint) GetMac() string { 89 | return c.Mac 90 | } 91 | 92 | func (c mockFirewallEndpoint) GetIP() net.IP { 93 | return c.IP 94 | } 95 | 96 | func makeMockStore() firewallStore { 97 | mockStore := firewallStore{} 98 | db, _ := sql.Open("sqlite3", "/tmp/firewall.db") 99 | 100 | createTable := `CREATE TABLE IF NOT EXISTS iptables_rules ( 101 | body TEXT PRIMARY KEY, 102 | state VARCHAR 103 | )` 104 | db.Exec(createTable) 105 | deleteFromTable := `DELETE FROM iptables_rules` 106 | db.Exec(deleteFromTable) 107 | 108 | mockStore.mu = new(sync.RWMutex) 109 | mockStore.db = db 110 | return mockStore 111 | } 112 | -------------------------------------------------------------------------------- /agent/hosts.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package agent 17 | 18 | import ( 19 | "github.com/romana/core/common/api" 20 | ) 21 | 22 | // IpamHosts is a collection of hosts with Get method. 23 | type IpamHosts []api.Host 24 | 25 | func (hosts IpamHosts) GetHost(hostname string) *api.Host { 26 | for hid, h := range hosts { 27 | if h.Name == hostname { 28 | return &hosts[hid] 29 | } 30 | } 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /agent/interface.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package agent 17 | 18 | import ( 19 | "fmt" 20 | "net" 21 | 22 | log "github.com/romana/rlog" 23 | "github.com/vishvananda/netlink" 24 | "golang.org/x/sys/unix" 25 | ) 26 | 27 | func CreateRomanaGW() error { 28 | rgw := &netlink.Dummy{LinkAttrs: netlink.LinkAttrs{Name: "romana-lo", TxQLen: 1000}} 29 | if err := netlink.LinkAdd(rgw); err != nil { 30 | if err == unix.EEXIST { 31 | log.Warn("Romana gateway already exists.") 32 | } else { 33 | log.Info("Error adding Romana gateway to node:", err) 34 | return err 35 | } 36 | } else { 37 | log.Info("Successfully added romana gateway to node.") 38 | } 39 | 40 | if err := netlink.LinkSetUp(rgw); err != nil { 41 | log.Error("Error while brining up romana gateway:", err) 42 | return err 43 | } 44 | 45 | return nil 46 | } 47 | 48 | func SetRomanaGwIP(romanaIp string) error { 49 | nip := net.ParseIP(romanaIp) 50 | if nip == nil { 51 | return fmt.Errorf("Failed to parse ip address %s", romanaIp) 52 | } 53 | 54 | ipnet := &net.IPNet{IP: nip, Mask: net.IPMask([]byte{0xff, 0xff, 0xff, 0xff})} 55 | 56 | link, err := netlink.LinkByName("romana-lo") 57 | if err != nil { 58 | return err 59 | } 60 | 61 | addrs, err := netlink.AddrList(link, unix.AF_INET) 62 | if err != nil { 63 | return err 64 | } 65 | 66 | for _, addr := range addrs { 67 | if addr.IPNet.String() == ipnet.String() { 68 | log.Debugf("Address %s already installed in interface %s", addr, link) 69 | return nil 70 | } 71 | } 72 | 73 | ip := &netlink.Addr{ 74 | IPNet: ipnet, 75 | } 76 | 77 | err = netlink.AddrAdd(link, ip) 78 | if err != nil { 79 | return err 80 | } 81 | 82 | return nil 83 | } 84 | -------------------------------------------------------------------------------- /agent/iptsave/iptablesSave/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package main 17 | 18 | import ( 19 | "flag" 20 | "fmt" 21 | "os" 22 | 23 | "github.com/romana/core/agent/iptsave" 24 | ) 25 | 26 | func main() { 27 | flag.Parse() 28 | 29 | ipt := iptsave.IPtables{} 30 | ipt.Parse(os.Stdin) 31 | 32 | fmt.Println(ipt.Render()) 33 | } 34 | -------------------------------------------------------------------------------- /agent/iptsave/iptables_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // Tests for iptsave library. 17 | package iptsave 18 | 19 | // Some comments on use of mocking framework in helpers_test.go. 20 | 21 | import ( 22 | "bufio" 23 | "bytes" 24 | "testing" 25 | ) 26 | 27 | func makeChains() (srcChain, dstChain IPchain) { 28 | dstChain = IPchain{ 29 | Name: "Dest", 30 | Rules: []*IPrule{ 31 | &IPrule{Action: IPtablesAction{Body: "Common"}}, 32 | &IPrule{Action: IPtablesAction{Body: "uniqDest"}}, 33 | &IPrule{Action: IPtablesAction{Body: "Common2"}}, 34 | }, 35 | } 36 | 37 | srcChain = IPchain{ 38 | Name: "Src", 39 | Rules: []*IPrule{ 40 | &IPrule{Action: IPtablesAction{Body: "Common"}}, 41 | &IPrule{Action: IPtablesAction{Body: "Common2"}}, 42 | &IPrule{Action: IPtablesAction{Body: "UniqSrc"}}, 43 | }, 44 | } 45 | 46 | return 47 | } 48 | 49 | func TestDiffRules(t *testing.T) { 50 | srcChain, dstChain := makeChains() 51 | 52 | uniqDest, uniqSrc, common := DiffRules(dstChain.Rules, srcChain.Rules) 53 | 54 | for _, r := range uniqSrc { 55 | t.Logf("src --> %s\n", r) 56 | } 57 | 58 | for _, r := range uniqDest { 59 | t.Logf("dest --> %s\n", r) 60 | } 61 | 62 | for _, r := range common { 63 | t.Logf("comm --> %s\n", r) 64 | } 65 | 66 | if len(uniqSrc) != 1 { 67 | t.Errorf("Expecting exactly 1 entry in source chain, got %d", len(uniqSrc)) 68 | } 69 | 70 | if len(uniqDest) != 1 { 71 | t.Errorf("Expecting exactly 1 entry in dest chain, got %d", len(uniqDest)) 72 | } 73 | 74 | if len(common) != 2 { 75 | t.Errorf("Expecting exactly 2 entries in common chain, got %d", len(common)) 76 | } 77 | 78 | } 79 | 80 | func TestMergeRules(t *testing.T) { 81 | srcChain, dstChain := makeChains() 82 | newRules := MergeChains(&dstChain, &srcChain) 83 | 84 | for _, r := range dstChain.Rules { 85 | t.Logf("new dest --> %s\n", r) 86 | } 87 | 88 | for _, r := range newRules { 89 | t.Logf("new rules --> %s\n", r) 90 | } 91 | 92 | if len(dstChain.Rules) != 4 { 93 | t.Errorf("Expecting exactly 4 entries in dest chain, got %d", len(dstChain.Rules)) 94 | } 95 | 96 | } 97 | 98 | func TestRuleParser(t *testing.T) { 99 | rule := "MYCHAIN -p tcp --dport 55 ! -p tcp --sport 80 -j TARGET" 100 | reader := bufio.NewReader(bytes.NewReader([]byte(rule))) 101 | chain := ParseRule(reader) 102 | if chain.Name != "MYCHAIN" || chain.Rules[0].String() != "-p tcp --dport 55 ! -p tcp --sport 80 -j TARGET" { 103 | t.Errorf("%s\n%s", chain.Name, chain.Rules[0].String()) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /agent/iptsave/token.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // Provides a definition of Item for the Lexer. 17 | package iptsave 18 | 19 | import ( 20 | "fmt" 21 | ) 22 | 23 | type Item struct { 24 | Type ItemType 25 | Body string 26 | } 27 | 28 | type ItemType int 29 | 30 | const ( 31 | ItemError ItemType = iota 32 | ItemEOF 33 | itemComment 34 | itemTable 35 | itemChain 36 | itemChainPolicy 37 | itemChainCounter 38 | itemRule 39 | itemRuleMatch 40 | itemModule 41 | itemAction 42 | itemOptions 43 | itemCommit 44 | ) 45 | 46 | func (i ItemType) String() string { 47 | switch i { 48 | case ItemError: 49 | return fmt.Sprintf("Error") 50 | case ItemEOF: 51 | return fmt.Sprintf("EOF") 52 | case itemComment: 53 | return fmt.Sprintf("Comment") 54 | case itemTable: 55 | return fmt.Sprintf("Table") 56 | case itemChain: 57 | return fmt.Sprintf("Chain") 58 | case itemChainPolicy: 59 | return fmt.Sprintf("ChainPolicy") 60 | case itemChainCounter: 61 | return fmt.Sprintf("ChainCounter") 62 | case itemRule: 63 | return fmt.Sprintf("Rule") 64 | case itemRuleMatch: 65 | return fmt.Sprintf("RuleMatch") 66 | case itemModule: 67 | return fmt.Sprintf("Module") 68 | case itemAction: 69 | return fmt.Sprintf("Action") 70 | case itemOptions: 71 | return fmt.Sprintf("Options") 72 | case itemCommit: 73 | return fmt.Sprintf("Commit") 74 | default: 75 | return fmt.Sprintf("Unknown item") 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /agent/metrics.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package agent 17 | 18 | import ( 19 | "fmt" 20 | "net/http" 21 | 22 | "github.com/prometheus/client_golang/prometheus" 23 | "github.com/prometheus/client_golang/prometheus/promhttp" 24 | "github.com/romana/core/agent/enforcer" 25 | log "github.com/romana/rlog" 26 | ) 27 | 28 | var ( 29 | NumManagedRoutes = prometheus.NewGauge( 30 | prometheus.GaugeOpts{ 31 | Name: "romana_managed_routes", 32 | Help: "Number of routes managed by Romana agent on the host.", 33 | }, 34 | ) 35 | ) 36 | 37 | func MetricStart(port int) error { 38 | if port <= 0 { 39 | return nil 40 | } 41 | 42 | registry := prometheus.NewRegistry() 43 | err := enforcer.MetricsRegister(registry) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | err = registry.Register(NumManagedRoutes) 49 | if err != nil { 50 | return err 51 | } 52 | 53 | handler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{ErrorHandling: promhttp.HTTPErrorOnError}) 54 | 55 | go func() { 56 | http.Handle("/", handler) 57 | log.Errorf("Metrics publishing stopped due to %s", http.ListenAndServe(fmt.Sprintf(":%d", port), nil)) 58 | }() 59 | 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /agent/policycache/policy_storage.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package policycache 17 | 18 | import ( 19 | "github.com/romana/core/agent/cache" 20 | "github.com/romana/core/common/api" 21 | ) 22 | 23 | type Interface interface { 24 | Put(string, api.Policy) 25 | Get(string) (api.Policy, bool) 26 | Delete(string) 27 | List() []api.Policy 28 | Keys() []string 29 | } 30 | 31 | type PolicyStorage struct { 32 | store cache.Interface 33 | } 34 | 35 | func New() Interface { 36 | return &PolicyStorage{cache.New()} 37 | } 38 | 39 | func (p *PolicyStorage) Put(key string, policy api.Policy) { 40 | p.store.Put(key, policy) 41 | } 42 | 43 | func (p *PolicyStorage) Get(key string) (api.Policy, bool) { 44 | item, ok := p.store.Get(key) 45 | if !ok { 46 | return api.Policy{}, ok 47 | } 48 | 49 | policy, ok := item.(api.Policy) 50 | if !ok { 51 | return api.Policy{}, ok 52 | } 53 | 54 | return policy, ok 55 | } 56 | 57 | func (p *PolicyStorage) List() []api.Policy { 58 | var result []api.Policy 59 | items := p.store.List() 60 | for _, item := range items { 61 | policy, ok := item.(api.Policy) 62 | if !ok { 63 | continue 64 | } 65 | result = append(result, policy) 66 | } 67 | return result 68 | } 69 | 70 | func (p *PolicyStorage) Keys() []string { 71 | return p.store.Keys() 72 | } 73 | 74 | func (p *PolicyStorage) Delete(key string) { 75 | p.store.Delete(key) 76 | } 77 | -------------------------------------------------------------------------------- /agent/policycontroller/controller.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package policycontroller 17 | 18 | import ( 19 | "context" 20 | "encoding/json" 21 | "time" 22 | 23 | "github.com/romana/core/agent/policycache" 24 | "github.com/romana/core/common/api" 25 | "github.com/romana/core/common/client" 26 | 27 | "github.com/docker/libkv/store" 28 | "github.com/pkg/errors" 29 | log "github.com/romana/rlog" 30 | ) 31 | 32 | const ( 33 | defaultWatcherReconnectTime = 5 * time.Second 34 | ) 35 | 36 | func Run(ctx context.Context, key string, client *client.Client, storage policycache.Interface) (<-chan api.Policy, error) { 37 | policies, err := client.Store.GetExt(key, store.GetOptions{Recursive: true}) 38 | if err != nil { 39 | return nil, errors.Wrap(err, "controller init fail") 40 | } 41 | 42 | for _, val := range policies.GetResponse().Node.Nodes { 43 | var policy api.Policy 44 | err := json.Unmarshal([]byte(val.Value), &policy) 45 | if err != nil { 46 | return nil, errors.Wrap(err, "failed to unmarshal policy") 47 | } 48 | 49 | storage.Put(val.Key, policy) 50 | } 51 | 52 | respCh, err := client.Store.WatchExt( 53 | key, store.WatcherOptions{Recursive: true, NoList: true}, ctx.Done()) 54 | if err != nil { 55 | return nil, errors.Wrap(err, "failed to start watching") 56 | } 57 | 58 | updateStorage := func(action, key string, policy api.Policy) { 59 | switch action { 60 | case "set", "update", "create", "compareAndSwap": 61 | storage.Put(key, policy) 62 | case "delete": 63 | storage.Delete(key) 64 | } 65 | } 66 | 67 | policyOut := make(chan api.Policy) 68 | var LastIndex uint64 69 | go func() { 70 | var err error 71 | for { 72 | if err != nil { 73 | log.Errorf("policy watcher store error: %s", err) 74 | // if we can't connect to the kvstore, wait for 75 | // few seconds and try reconnecting. 76 | time.Sleep(defaultWatcherReconnectTime) 77 | respCh, _ = client.Store.WatchExt( 78 | key, 79 | store.WatcherOptions{Recursive: true, 80 | NoList: true, 81 | AfterIndex: LastIndex, 82 | }, 83 | ctx.Done()) 84 | } 85 | 86 | select { 87 | case <-ctx.Done(): 88 | log.Printf("Stopping policy watcher module.") 89 | return 90 | 91 | case resp, ok := <-respCh: 92 | if !ok || resp == nil { 93 | err = errors.New("kvstore policy events channel closed") 94 | continue 95 | } 96 | 97 | LastIndex = resp.LastIndex 98 | var p api.Policy 99 | 100 | value := resp.Value 101 | if resp.Action == "delete" { 102 | value = resp.PrevValue 103 | } 104 | 105 | if errp := json.Unmarshal([]byte(value), &p); errp != nil { 106 | log.Printf("failed to unmarshal policy %v, err=%s", value, errp) 107 | continue 108 | } 109 | 110 | updateStorage(resp.Action, resp.Key, p) 111 | policyOut <- p 112 | } 113 | 114 | } 115 | }() 116 | 117 | return policyOut, nil 118 | } 119 | -------------------------------------------------------------------------------- /agent/policyhasher/hasher.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package policyhasher 17 | 18 | import ( 19 | "crypto/sha1" 20 | "encoding/hex" 21 | "fmt" 22 | "strings" 23 | 24 | "github.com/romana/core/common/api" 25 | ) 26 | 27 | // HashRomanaPolicies generates unique hash for a list of romana policies. 28 | func HashRomanaPolicies(policies []api.Policy) string { 29 | var hashes []string 30 | for _, policy := range policies { 31 | hashes = append(hashes, HashRomanaPolicy(policy)) 32 | } 33 | 34 | return HashListOfStrings(hashes) 35 | } 36 | 37 | // HashRomanaPolicies generates sha1 hash from a canonical form of the policy. 38 | func HashRomanaPolicy(policy api.Policy) string { 39 | sorted := PolicyToCanonical(policy) 40 | 41 | var data string 42 | 43 | data = fmt.Sprintf("%s.%s.%s", policy.Direction, policy.Description, policy.ID) 44 | 45 | for _, e := range sorted.AppliedTo { 46 | data = fmt.Sprintf("%s.%s", data, EndpointToString(e)) 47 | } 48 | 49 | for _, i := range sorted.Ingress { 50 | for _, e := range i.Peers { 51 | data = fmt.Sprintf("%s.%s", data, EndpointToString(e)) 52 | } 53 | 54 | for _, r := range i.Rules { 55 | data = fmt.Sprintf("%s.%s%d%d%t", data, r.Protocol, r.IcmpType, r.IcmpCode, r.IsStateful) 56 | 57 | for _, p := range r.Ports { 58 | data = fmt.Sprintf("%s%d", data, p) 59 | } 60 | 61 | for _, p := range r.PortRanges { 62 | data = fmt.Sprintf("%s%d%d", data, p[0], p[1]) 63 | } 64 | } 65 | } 66 | 67 | hasher := sha1.New() 68 | hasher.Write([]byte(data)) 69 | sum := hasher.Sum(nil) 70 | 71 | return fmt.Sprint(hex.EncodeToString(sum)) 72 | 73 | } 74 | 75 | // HashListOfStrings generates sha1 hash from a list of strings. 76 | func HashListOfStrings(hashes []string) string { 77 | data := strings.Join(hashes, "") 78 | hasher := sha1.New() 79 | hasher.Write([]byte(data)) 80 | sum := hasher.Sum(nil) 81 | 82 | return fmt.Sprint(hex.EncodeToString(sum)) 83 | } 84 | -------------------------------------------------------------------------------- /agent/policyhasher/hasher_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // +build ignore 17 | 18 | package policyhasher 19 | 20 | import ( 21 | "testing" 22 | 23 | "github.com/romana/core/common" 24 | ) 25 | 26 | func TestNewEndpointList(t *testing.T) { 27 | 28 | endpointList := []common.Endpoint{ 29 | common.Endpoint{ 30 | Peer: "Foo", 31 | Dest: "Bar", 32 | }, 33 | common.Endpoint{ 34 | TenantName: "alice", 35 | TenantNetworkID: nil, 36 | }, 37 | common.Endpoint{ 38 | Peer: "Dead", 39 | Dest: "Beef", 40 | }, 41 | } 42 | 43 | t.Logf("Before sort %v", endpointList) 44 | newList := NewEndpointList(endpointList).Sort().List() 45 | t.Logf("After sort %v", newList) 46 | 47 | if newList[2].Peer != "Foo" { 48 | t.Errorf("Unexpected result from NewEndpointList, %v", newList) 49 | } 50 | } 51 | 52 | func TestSortRules(t *testing.T) { 53 | ruleList := []common.Rule{ 54 | common.Rule{ 55 | Protocol: "TCP", 56 | Ports: []uint{1, 2, 3}, 57 | }, 58 | common.Rule{ 59 | Protocol: "ICMP", 60 | IcmpType: uint(7), 61 | IcmpCode: uint(8), 62 | }, 63 | common.Rule{ 64 | Protocol: "TCP", 65 | PortRanges: []common.PortRange{ 66 | common.PortRange{0, 9}, 67 | common.PortRange{8, 2}, 68 | }, 69 | }, 70 | common.Rule{ 71 | Protocol: "UDP", 72 | Ports: []uint{6, 5, 4}, 73 | }, 74 | } 75 | 76 | t.Logf("Before sort %v", ruleList) 77 | newList := RulesToCanonical(ruleList) 78 | t.Logf("After sort %v", newList) 79 | 80 | if newList[3].Ports[2] != 6 { 81 | t.Errorf("Unexpected result from SortRules, %v", newList) 82 | } 83 | } 84 | 85 | func TestHashRomanaPolicies(t *testing.T) { 86 | policyList := []common.Policy{ 87 | common.Policy{ 88 | Direction: "ingress", 89 | Description: "test policy one", 90 | Name: "one", 91 | ExternalID: "foo", 92 | AppliedTo: []common.Endpoint{ 93 | common.Endpoint{ 94 | Peer: "Host", 95 | Dest: "Local", 96 | }, 97 | }, 98 | Ingress: []common.RomanaIngress{ 99 | common.RomanaIngress{ 100 | Peers: []common.Endpoint{ 101 | common.Endpoint{ 102 | TenantName: "Alice", 103 | SegmentName: "AliceVille", 104 | }, 105 | }, 106 | Rules: []common.Rule{ 107 | common.Rule{ 108 | Protocol: "TCP", 109 | Ports: []uint{5, 4, 3}, 110 | }, 111 | }, 112 | }, 113 | }, 114 | }, 115 | common.Policy{ 116 | Direction: "ingress", 117 | Description: "test policy two", 118 | Name: "two", 119 | ExternalID: "bar", 120 | AppliedTo: []common.Endpoint{ 121 | common.Endpoint{ 122 | Peer: "Local", 123 | Dest: "Host", 124 | }, 125 | }, 126 | Ingress: []common.RomanaIngress{ 127 | common.RomanaIngress{ 128 | Peers: []common.Endpoint{ 129 | common.Endpoint{ 130 | TenantName: "Bob", 131 | SegmentName: "BobHold", 132 | }, 133 | }, 134 | Rules: []common.Rule{ 135 | common.Rule{ 136 | Protocol: "TCP", 137 | Ports: []uint{9, 8, 7}, 138 | }, 139 | }, 140 | }, 141 | }, 142 | }, 143 | } 144 | 145 | hash := HashRomanaPolicies(policyList) 146 | if hash != "fcfe27cb94edeb6cdb186192ed32e1c83374d3a5" { 147 | t.Errorf("Unexpected result from HashRomanaPolicies %s", hash) 148 | } else { 149 | t.Logf("Hash is %s", hash) 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /agent/routes.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package agent 17 | 18 | import ( 19 | "fmt" 20 | "net" 21 | 22 | "github.com/pkg/errors" 23 | "github.com/romana/core/common/api" 24 | log "github.com/romana/rlog" 25 | "github.com/vishvananda/netlink" 26 | ) 27 | 28 | // CreateRouteToBlocks loops over list of blocks and creates routes when needed. 29 | func CreateRouteToBlocks(blocks []api.IPAMBlockResponse, 30 | hosts IpamHosts, 31 | romanaRouteTableId int, 32 | hostname string, 33 | multihop bool, 34 | nlHandle nlHandleRoute) { 35 | 36 | var managedRoutes int 37 | for _, block := range blocks { 38 | if block.Host == hostname { 39 | log.Debugf("Block %v is local and does not require a route on that host", block) 40 | continue 41 | } 42 | 43 | host := hosts.GetHost(block.Host) 44 | if host == nil { 45 | log.Debugf("Block %v belongs to unknown host %s, ignoring", block, block.Host) 46 | continue 47 | } 48 | 49 | if err := createRouteToBlock(block, host, romanaRouteTableId, multihop, nlHandle); err != nil { 50 | _, ok := err.(RouteAdjacencyError) 51 | if ok { 52 | // Lower severity for expected error 53 | log.Tracef(4, "%s", err) 54 | continue 55 | } 56 | 57 | log.Errorf("%s", err) 58 | continue 59 | } 60 | managedRoutes += 1 61 | } 62 | 63 | NumManagedRoutes.Set(float64(managedRoutes)) 64 | } 65 | 66 | type nlHandleRoute interface { 67 | RouteGet(net.IP) ([]netlink.Route, error) 68 | RouteAdd(*netlink.Route) error 69 | } 70 | 71 | // createRouteToBlock creates ip route for given block->host pair in Romana routing table, 72 | // the function will fail if requested block is not directly adjacent and multihop false. 73 | func createRouteToBlock(block api.IPAMBlockResponse, host *api.Host, romanaRouteTableId int, multihop bool, nlHandle nlHandleRoute) error { 74 | testRoutes, err := nlHandle.RouteGet(host.IP) 75 | if err != nil { 76 | return errors.Wrapf(err, "couldn't test host %s adjacency", host.IP) 77 | } 78 | 79 | if len(testRoutes) > 1 { 80 | return errors.New(fmt.Sprintf("more then one path available for host %s, multipath not currently supported", host.IP)) 81 | } 82 | 83 | if len(testRoutes) == 0 { 84 | return errors.New(fmt.Sprintf("no way to reach %s, no default gateway?", host.IP)) 85 | } 86 | 87 | if testRoutes[0].Gw != nil && multihop == false { 88 | return RouteAdjacencyError{} 89 | } 90 | 91 | route := netlink.Route{ 92 | Dst: &block.CIDR.IPNet, 93 | Gw: host.IP, 94 | Table: romanaRouteTableId, 95 | } 96 | 97 | log.Debugf("About to create route %v", route) 98 | return nlHandle.RouteAdd(&route) 99 | } 100 | 101 | type RouteAdjacencyError struct{} 102 | 103 | func (RouteAdjacencyError) Error() string { 104 | return "no directly adjacent route and multihop is prohibited" 105 | } 106 | -------------------------------------------------------------------------------- /agent/rtable/route_table.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package rtable 17 | 18 | import ( 19 | "bufio" 20 | "fmt" 21 | "os" 22 | "os/exec" 23 | 24 | "github.com/pkg/errors" 25 | "github.com/romana/rlog" 26 | "github.com/vishvananda/netlink" 27 | "golang.org/x/sys/unix" 28 | ) 29 | 30 | const ( 31 | RT_TABLES_FILE = "/etc/iproute2/rt_tables" 32 | ) 33 | 34 | // EnsureRouteTableExist verifies that romana route table with appropriate index 35 | // exist in RT_TABLES_FILE file. 36 | func EnsureRouteTableExist(routeTableId int) (err error) { 37 | 38 | file, err := os.OpenFile(RT_TABLES_FILE, os.O_RDWR, 0644) 39 | if err != nil { 40 | return errors.Wrapf(err, 41 | "failed to open file %s to ensure that romana table name is configured", 42 | RT_TABLES_FILE) 43 | } 44 | defer func() { 45 | if err2 := file.Close(); err2 != nil { 46 | err = errors.Wrapf(err, "couldn't close the file %s", err) 47 | } 48 | }() 49 | 50 | tableName := "romana" 51 | err = ensureRouteTableName(file, tableName, routeTableId) 52 | if err != nil { 53 | return errors.Wrap(err, "couldn't verify that romana table name is configured") 54 | } 55 | return nil 56 | 57 | } 58 | 59 | func ensureRouteTableName(file *os.File, tableName string, routeTableId int) error { 60 | targetEntry := fmt.Sprintf("%d %s", routeTableId, tableName) 61 | 62 | scanner := bufio.NewScanner(file) 63 | for scanner.Scan() { 64 | if scanner.Text() == targetEntry { 65 | return nil 66 | } 67 | } 68 | 69 | _, err := file.Write([]byte(targetEntry + "\n")) 70 | if err != nil { 71 | return err 72 | } 73 | 74 | return nil 75 | } 76 | 77 | // nlRuleHandle subset of netlink.Handle methods isolated for mocking. 78 | type nlRuleHandle interface { 79 | RuleList(family int) ([]netlink.Rule, error) 80 | RuleAdd(*netlink.Rule) error 81 | } 82 | 83 | // EnsureRomanaRouteRule verifies that rule for romana routing table installed. 84 | func EnsureRomanaRouteRule(romanaRouteTableId int, nl nlRuleHandle) error { 85 | rules, err := nl.RuleList(unix.AF_INET) 86 | if err != nil { 87 | return err 88 | } 89 | 90 | for _, rule := range rules { 91 | if rule.Table == romanaRouteTableId { 92 | return nil 93 | } 94 | } 95 | 96 | inRule := netlink.NewRule() 97 | inRule.Table = romanaRouteTableId 98 | 99 | rlog.Infof("Adding routing rule %v", inRule) 100 | err = nl.RuleAdd(inRule) 101 | if err != nil { 102 | return err 103 | } 104 | 105 | return nil 106 | } 107 | 108 | // FlushRomanaTable attempts to delete all routes from table called romana. 109 | func FlushRomanaTable() error { 110 | command := exec.Command("ip", "ro", "flush", "table", "romana") 111 | 112 | out, err := command.CombinedOutput() 113 | if err != nil { 114 | return fmt.Errorf("failed to flush romana route table out=%s, err=%s", string(out), err) 115 | } 116 | 117 | return nil 118 | 119 | } 120 | -------------------------------------------------------------------------------- /agent/rtable/testdata/rt_tables.case1: -------------------------------------------------------------------------------- 1 | # 2 | # reserved values 3 | # 4 | 255 local 5 | 254 main 6 | 253 default 7 | 0 unspec 8 | # 9 | # local 10 | # 11 | #1 inr.ruhep 12 | -------------------------------------------------------------------------------- /agent/rtable/testdata/rt_tables.golden: -------------------------------------------------------------------------------- 1 | # 2 | # reserved values 3 | # 4 | 255 local 5 | 254 main 6 | 253 default 7 | 0 unspec 8 | # 9 | # local 10 | # 11 | #1 inr.ruhep 12 | 10 romana 13 | -------------------------------------------------------------------------------- /agent/sysctl/sysctl.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package sysctl 17 | 18 | import ( 19 | "bytes" 20 | "io/ioutil" 21 | "strings" 22 | ) 23 | 24 | // Set systcl value to 1, the specified path has to start with /proc/sys. 25 | // Example: /proc/sys/net/ipv4/conf/default/proxy_arp 26 | func Set(path string) error { 27 | if !strings.HasPrefix(path, "/proc/sys") { 28 | return errorSetBoundary("systl.Set() called with path outside of /proc/sys") 29 | } 30 | 31 | return ioutil.WriteFile(path, []byte("1"), 0644) 32 | } 33 | 34 | // errorSetBoundary indicates that Set function got called 35 | // with invalid path. 36 | type errorSetBoundary string 37 | 38 | func (e errorSetBoundary) Error() string { return string(e) } 39 | 40 | // Check that sysctl value is 1, the specified path has to start with /proc/sys. 41 | // For paths outside of /proc/sys result is undetermented. 42 | func Check(path string) (bool, error) { 43 | data, err := ioutil.ReadFile(path) 44 | if err != nil { 45 | return false, err 46 | } 47 | 48 | if bytes.Equal(data, []byte("1\n")) { 49 | return true, nil 50 | } 51 | 52 | return false, nil 53 | } 54 | -------------------------------------------------------------------------------- /agent/sysctl/sysctl_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package sysctl 17 | 18 | import ( 19 | "path/filepath" 20 | "testing" 21 | ) 22 | 23 | const testFileDir = "testdata" 24 | 25 | func TestCheck(t *testing.T) { 26 | cases := []struct { 27 | name, path string 28 | expect, expectErr bool 29 | }{ 30 | {"check positive", "sysctl.golden", true, false}, 31 | {"check negative", "sysctl.zero", false, false}, 32 | {"check fail", "none", false, true}, 33 | } 34 | 35 | for _, tc := range cases { 36 | t.Run(tc.name, func(t *testing.T) { 37 | ok, err := Check(filepath.Join(testFileDir, tc.path)) 38 | if err != nil && tc.expectErr == false { 39 | t.Errorf("Received unexpected error %s", err) 40 | } 41 | 42 | if ok != tc.expect { 43 | t.Errorf("Unexpected return value, expected %t, got %t", tc.expect, ok) 44 | } 45 | }) 46 | } 47 | } 48 | 49 | func TestSet(t *testing.T) { 50 | err := Set("/tmp/doesnotexist") 51 | if err == nil { 52 | t.Fatalf("didn't fail when expected") 53 | } 54 | 55 | if _, ok := err.(errorSetBoundary); !ok { 56 | t.Fatalf("received unexpected error type %s.(%T)", err, err) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /agent/sysctl/testdata/sysctl.golden: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /agent/sysctl/testdata/sysctl.zero: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romana/core/6139692fadcb6b4b67767576809c223f557aef47/agent/sysctl/testdata/sysctl.zero -------------------------------------------------------------------------------- /cli/adaptor/client.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // Package adaptor implements glue code for multiple 17 | // platforms like openstack, kubernetes, etc. It 18 | // interfaces directly with REST API for them and 19 | // allows same interface for other command line tools. 20 | package adaptor 21 | 22 | import ( 23 | "github.com/romana/core/cli/kubernetes" 24 | "github.com/romana/core/cli/openstack" 25 | "github.com/romana/core/cli/util" 26 | 27 | config "github.com/spf13/viper" 28 | ) 29 | 30 | // GetTenantName returns platform specific tenant name 31 | // corresponding to the UUID being used in romana tenants. 32 | func GetTenantName(uuid string) (string, error) { 33 | if platform := config.GetString("Platform"); platform == "kubernetes" { 34 | return kubernetes.GetTenantName(uuid) 35 | } else if platform == "openstack" { 36 | return openstack.GetTenantName(uuid) 37 | } else if platform == "mesos" { 38 | return "", util.ErrUnimplementedPlatform 39 | } 40 | return "", util.ErrInvalidPlatform 41 | } 42 | 43 | // TenantExists returns true/false depending on platform 44 | // specific tenant name or uuid exists or not. 45 | func TenantExists(name string) bool { 46 | if platform := config.GetString("Platform"); platform == "kubernetes" { 47 | return kubernetes.TenantExists(name) 48 | } else if platform == "openstack" { 49 | return openstack.TenantExists(name) 50 | } else if platform == "mesos" { 51 | // Unimplemented Platform. 52 | return false 53 | } 54 | // Error 55 | return false 56 | } 57 | 58 | // GetTenantUUID returns platform specific tenant UUID 59 | // corresponding to the name. 60 | func GetTenantUUID(name string) (string, error) { 61 | if platform := config.GetString("Platform"); platform == "kubernetes" { 62 | return kubernetes.GetTenantUUID(name) 63 | } else if platform == "openstack" { 64 | return openstack.GetTenantUUID(name) 65 | } else if platform == "mesos" { 66 | return "", util.ErrUnimplementedPlatform 67 | } 68 | return "", util.ErrInvalidPlatform 69 | } 70 | 71 | // CreateTenant creates platform specific tenant 72 | // corresponding to the name given. 73 | func CreateTenant(name string) error { 74 | if platform := config.GetString("Platform"); platform == "kubernetes" { 75 | return kubernetes.CreateTenant(name) 76 | } else if platform == "openstack" { 77 | return openstack.CreateTenant(name) 78 | } else if platform == "mesos" { 79 | return util.ErrUnimplementedPlatform 80 | } 81 | return util.ErrInvalidPlatform 82 | } 83 | -------------------------------------------------------------------------------- /cli/commands/docs.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // Package commands contains various files for adding commands and subcommands 17 | // to romana command line tools. 18 | // 19 | // Each file in the package directory contains exactly one command and 20 | // multiple sub-commands for it, except the root.go file which initializes 21 | // the config and cli package and sets various command line parameters. 22 | // 23 | // The package used for configuration is called viper and it allows 24 | // configuration to be read from configuration files or to be set from 25 | // command line parameters. 26 | // 27 | // The package used for command line interface is called cobra and it 28 | // is the main library behind providing usage, flag support and various 29 | // other command line features. 30 | // 31 | package commands 32 | -------------------------------------------------------------------------------- /cli/commands/host.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package commands 17 | 18 | import ( 19 | "encoding/json" 20 | "fmt" 21 | "net/http" 22 | "os" 23 | "text/tabwriter" 24 | 25 | "github.com/romana/core/common/api" 26 | 27 | "github.com/go-resty/resty" 28 | cli "github.com/spf13/cobra" 29 | config "github.com/spf13/viper" 30 | ) 31 | 32 | // hostCmd represents the host commands 33 | var hostCmd = &cli.Command{ 34 | Use: "host [add|show|list|remove]", 35 | Short: "Add, Remove or Show hosts for romana services.", 36 | Long: `Add, Remove or Show hosts for romana services. 37 | 38 | host requires a subcommand, e.g. ` + "`romana host add`." + ` 39 | 40 | For more information, please check http://romana.io 41 | `, 42 | } 43 | 44 | func init() { 45 | hostCmd.AddCommand(hostAddCmd) 46 | hostCmd.AddCommand(hostShowCmd) 47 | hostCmd.AddCommand(hostListCmd) 48 | hostCmd.AddCommand(hostRemoveCmd) 49 | } 50 | 51 | var hostAddCmd = &cli.Command{ 52 | Use: "add [hostip][(optional)romana cidr][(optional)agent port]", 53 | Short: "Add a new host.", 54 | Long: `Add a new host.`, 55 | RunE: hostAdd, 56 | SilenceUsage: true, 57 | } 58 | 59 | var hostShowCmd = &cli.Command{ 60 | Use: "show [hostip1][hostip2]...", 61 | Short: "Show details for a specific host.", 62 | Long: `Show details for a specific host.`, 63 | RunE: hostShow, 64 | SilenceUsage: true, 65 | } 66 | 67 | var hostListCmd = &cli.Command{ 68 | Use: "list", 69 | Short: "List all hosts.", 70 | Long: `List all hosts.`, 71 | RunE: hostList, 72 | SilenceUsage: true, 73 | } 74 | 75 | var hostRemoveCmd = &cli.Command{ 76 | Use: "remove [hostip]", 77 | Short: "Remove a host.", 78 | Long: `Remove a host.`, 79 | RunE: hostRemove, 80 | SilenceUsage: true, 81 | } 82 | 83 | func hostAdd(cmd *cli.Command, args []string) error { 84 | fmt.Println("Unimplemented: Add host/s.") 85 | return nil 86 | } 87 | 88 | func hostShow(cmd *cli.Command, args []string) error { 89 | fmt.Println("Unimplemented: Show host details.") 90 | return nil 91 | } 92 | 93 | func hostList(cmd *cli.Command, args []string) error { 94 | rootURL := config.GetString("RootURL") 95 | resp, err := resty.R().Get(rootURL + "/hosts") 96 | if err != nil { 97 | return err 98 | } 99 | 100 | if config.GetString("Format") == "json" { 101 | JSONFormat(resp.Body(), os.Stdout) 102 | } else { 103 | w := tabwriter.NewWriter(os.Stdout, 0, 8, 0, '\t', 0) 104 | 105 | if resp.StatusCode() == http.StatusOK { 106 | var hosts api.HostList 107 | err := json.Unmarshal(resp.Body(), &hosts) 108 | if err == nil { 109 | fmt.Println("Host List") 110 | fmt.Fprintf(w, "Host IP\tHost Name\n") 111 | for _, host := range hosts.Hosts { 112 | fmt.Fprintf(w, "%s\t%s\n", 113 | host.IP.String(), 114 | host.Name, 115 | ) 116 | } 117 | } else { 118 | fmt.Printf("Error: %s \n", err) 119 | } 120 | } else { 121 | var e Error 122 | json.Unmarshal(resp.Body(), &e) 123 | 124 | fmt.Println("Host Error") 125 | fmt.Fprintf(w, "Fields\t%s\n", e.Fields) 126 | fmt.Fprintf(w, "Message\t%s\n", e.Message) 127 | fmt.Fprintf(w, "Status\t%d\n", resp.StatusCode()) 128 | } 129 | w.Flush() 130 | } 131 | 132 | return nil 133 | } 134 | 135 | func hostRemove(cmd *cli.Command, args []string) error { 136 | fmt.Println("Unimplemented: Remove a host.") 137 | return nil 138 | } 139 | -------------------------------------------------------------------------------- /cli/kubernetes/tenant.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // Package kubernetes implements kubernetes API specific 17 | // helper functions. 18 | package kubernetes 19 | 20 | import ( 21 | "github.com/romana/core/cli/util" 22 | ) 23 | 24 | // GetTenantName returns kubernetes tenant name corresponding 25 | // to the UUID being used in romana tenants. 26 | func GetTenantName(uid string) (string, error) { 27 | return "", util.ErrUnimplementedFeature 28 | } 29 | 30 | // TenantExists returns true/false depending on 31 | // kubernetes tenant name or uuid exists. 32 | func TenantExists(name string) bool { 33 | // Unimplemented 34 | return false 35 | } 36 | 37 | // GetTenantUUID returns kubernetes tenant 38 | // UUID corresponding to the name. 39 | func GetTenantUUID(name string) (string, error) { 40 | return "", util.ErrUnimplementedFeature 41 | } 42 | 43 | // CreateTenant creates kubernetes specific tenant 44 | // corresponding to the name given. 45 | func CreateTenant(name string) error { 46 | return util.ErrUnimplementedFeature 47 | } 48 | -------------------------------------------------------------------------------- /cli/romana.sample.yaml: -------------------------------------------------------------------------------- 1 | { 2 | # 3 | # Default romana configuration file. 4 | # please move it to ~/.romana.yaml 5 | # 6 | "RootURL": "http://192.168.99.10:9600", 7 | "LogFile": "/var/tmp/romana-cli.log", 8 | "Format": "table", # options are table/json 9 | "Platform": "kubernetes", # options are openstack/kubernetes 10 | "Verbose": false, 11 | } 12 | -------------------------------------------------------------------------------- /cli/util/error.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // Package util contains common utility functions. 17 | package util 18 | 19 | import ( 20 | "errors" 21 | ) 22 | 23 | var ( 24 | ErrTenantNotFound = errors.New("tenant not found") 25 | ErrUnimplementedFeature = errors.New("unimplemented feature") 26 | ErrInvalidPlatform = errors.New("invalid platform") 27 | ErrUnimplementedPlatform = errors.New("unimplemented platform") 28 | ) 29 | -------------------------------------------------------------------------------- /cli/util/helpers.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // Package util contains common utility functions. 17 | package util 18 | 19 | import ( 20 | "bytes" 21 | "encoding/json" 22 | "fmt" 23 | 24 | cli "github.com/spf13/cobra" 25 | ) 26 | 27 | // UsageError shows command line help for errors caused due 28 | // to few or more arguments being passed to the commands or 29 | // sub-commands of romana command line tools. 30 | func UsageError(cmd *cli.Command, format string, args ...interface{}) error { 31 | return fmt.Errorf("%s\nCheck '%s -h' for help", 32 | fmt.Sprintf(format, args...), 33 | cmd.CommandPath()) 34 | } 35 | 36 | // JSONIndent indents the JSON input string, return input string if it fails. 37 | func JSONIndent(inStr string) string { 38 | var out bytes.Buffer 39 | err := json.Indent(&out, []byte(inStr), "", "\t") 40 | if err != nil { 41 | return inStr 42 | } 43 | return out.String() 44 | } 45 | -------------------------------------------------------------------------------- /cmd/romana/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // Package main implements command line tools for various romana services. 17 | package main 18 | 19 | import ( 20 | "github.com/romana/core/cli/commands" 21 | ) 22 | 23 | func main() { 24 | commands.Execute() 25 | } 26 | -------------------------------------------------------------------------------- /cmd/romana_cni/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package main 17 | 18 | import ( 19 | "github.com/romana/core/cni" 20 | 21 | "github.com/containernetworking/cni/pkg/skel" 22 | "github.com/containernetworking/cni/pkg/version" 23 | ) 24 | 25 | func main() { 26 | skel.PluginMain(cni.CmdAdd, cni.CmdDel, version.All) 27 | } 28 | -------------------------------------------------------------------------------- /cmd/romana_doc/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package main 17 | 18 | import ( 19 | "fmt" 20 | "io/ioutil" 21 | "log" 22 | "os" 23 | 24 | "github.com/romana/core/common" 25 | "github.com/romana/core/doc/tools" 26 | "github.com/romana/core/server" 27 | ) 28 | 29 | func main() { 30 | 31 | if len(os.Args) != 2 { 32 | log.Fatalf("%s ", os.Args[0]) 33 | } 34 | path := os.Args[1] 35 | log.Printf("Analyzing %s", path) 36 | a := tools.NewAnalyzer(path) 37 | a.Analyze() 38 | 39 | // TODO this can be introspectable as well 40 | // serviceInterfaceName := "github.com/romana/core/common.Service" 41 | // implementors := a.FindImplementors(serviceInterfaceName) 42 | // log.Printf("The following implement the %s interface: %+v", serviceInterfaceName, implementors) 43 | 44 | services := []common.Service{&server.Romanad{}} 45 | for _, service := range services { 46 | rd := tools.NewSwaggerer(a, service) 47 | json, err := rd.Process() 48 | if err != nil { 49 | panic(err) 50 | } 51 | serviceName := service.Name() 52 | dir := fmt.Sprintf("doc/%s", serviceName) 53 | err = os.MkdirAll(dir, os.ModeDir|os.ModePerm) 54 | if err != nil { 55 | panic(err) 56 | } 57 | fname := fmt.Sprintf("%s/%s.yaml", dir, serviceName) 58 | err = ioutil.WriteFile(fname, json, 0644) 59 | if err != nil { 60 | panic(err) 61 | } 62 | log.Printf("Wrote %s", fname) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /cmd/romana_listener/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // Command for running the Kubernetes Listener. 17 | package main 18 | 19 | import ( 20 | "flag" 21 | "fmt" 22 | 23 | "os" 24 | "strings" 25 | 26 | "github.com/romana/core/common" 27 | "github.com/romana/core/common/client" 28 | "github.com/romana/core/listener" 29 | log "github.com/romana/rlog" 30 | ) 31 | 32 | func main() { 33 | endpointsStr := flag.String("etcd-endpoints", client.DefaultEtcdEndpoints, "Comma-separated list of etcd endpoints.") 34 | host := flag.String("host", "localhost", "Host to listen on.") 35 | port := flag.Int("port", 9602, "Port to listen on.") 36 | prefix := flag.String("etcd-prefix", client.DefaultEtcdPrefix, "Prefix to use for etcd data.") 37 | flag.Parse() 38 | 39 | fmt.Println(common.BuildInfo()) 40 | 41 | if endpointsStr == nil { 42 | log.Errorf("No etcd endpoints specified") 43 | os.Exit(1) 44 | } 45 | endpoints := strings.Split(*endpointsStr, ",") 46 | listener := &listener.KubeListener{Addr: fmt.Sprintf("%s:%d", *host, *port)} 47 | 48 | pr := *prefix 49 | if !strings.HasPrefix(pr, "/") { 50 | pr = "/" + pr 51 | } 52 | config := common.Config{EtcdEndpoints: endpoints, 53 | EtcdPrefix: pr, 54 | } 55 | svcInfo, err := common.InitializeService(listener, config) 56 | if err != nil { 57 | log.Error(err) 58 | os.Exit(2) 59 | } 60 | if svcInfo != nil { 61 | for { 62 | msg := <-svcInfo.Channel 63 | log.Info(msg) 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /cmd/romana_route_publisher/examples/endpoint-networks.template: -------------------------------------------------------------------------------- 1 | {{/* 2 | This is a template to that can be rendered by 3 | Romana route publisher into bird.conf 4 | 5 | It will export list of CIDRs (blocks) which 6 | are served by the current host. 7 | */}} 8 | protocol static static_bgp { 9 | {{range .Networks}} 10 | route {{.}} reject; 11 | {{end}} 12 | } 13 | 14 | protocol bgp bgp_def { 15 | export where proto = "static_bgp"; 16 | direct; 17 | local as {{.LocalAS}}; 18 | neighbor 192.168.99.1 as {{.LocalAS}}; 19 | } 20 | -------------------------------------------------------------------------------- /cmd/romana_route_publisher/examples/host-groups.template: -------------------------------------------------------------------------------- 1 | {{/* 2 | This is a template to that can be rendered by 3 | Romana route publisher into bird.conf 4 | 5 | It will export list of CIDRs (prefix groups) 6 | which current host is a part of. 7 | */}} 8 | {{ $hg := index .Args "HostGroups" -}} 9 | 10 | protocol static static_bgp { 11 | {{ range $net, $group := $hg }} 12 | # Network {{ $net }} 13 | route {{ $group.CIDR }} reject; 14 | {{ end }} 15 | } 16 | 17 | protocol bgp bgp_def { 18 | export where proto = "static_bgp"; 19 | direct; 20 | local as {{.LocalAS}}; 21 | neighbor 192.168.99.1 as {{.LocalAS}}; 22 | } 23 | -------------------------------------------------------------------------------- /cmd/romana_route_publisher/routes.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package main 17 | 18 | import ( 19 | "net" 20 | 21 | "github.com/romana/core/common/api" 22 | "github.com/romana/core/routepublisher/publisher" 23 | 24 | log "github.com/romana/rlog" 25 | ) 26 | 27 | // createRouteToBlocks loops over list of blocks and creates routes when needed. 28 | func createRouteToBlocks(blocks []api.IPAMBlockResponse, args map[string]interface{}, hostname string, bird publisher.Interface) { 29 | var networks []net.IPNet 30 | 31 | for _, block := range blocks { 32 | if block.Host != hostname { 33 | log.Tracef(4, "Block %v is remote and should not be advertised", block) 34 | continue 35 | } 36 | 37 | networks = append(networks, block.CIDR.IPNet) 38 | } 39 | 40 | err := bird.Update(networks, args) 41 | if err != nil { 42 | log.Error(err) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /cmd/romanad/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // Command for running the IPAM service. 17 | package main 18 | 19 | import ( 20 | "flag" 21 | "fmt" 22 | 23 | "os" 24 | "strings" 25 | 26 | "github.com/romana/core/common" 27 | "github.com/romana/core/common/client" 28 | "github.com/romana/core/server" 29 | log "github.com/romana/rlog" 30 | ) 31 | 32 | func main() { 33 | endpointsStr := flag.String("etcd-endpoints", client.DefaultEtcdEndpoints, "Comma-separated list of etcd endpoints.") 34 | host := flag.String("host", "localhost", "Host to listen on.") 35 | port := flag.Int("port", 9600, "Port to listen on.") 36 | prefix := flag.String("etcd-prefix", client.DefaultEtcdPrefix, "Prefix to use for etcd data.") 37 | topologyFile := flag.String("initial-topology-file", "", "Initial topology") 38 | flag.Parse() 39 | 40 | fmt.Println(common.BuildInfo()) 41 | 42 | if endpointsStr == nil { 43 | log.Errorf("No etcd endpoints specified") 44 | os.Exit(1) 45 | } 46 | endpoints := strings.Split(*endpointsStr, ",") 47 | romanad := &server.Romanad{Addr: fmt.Sprintf("%s:%d", *host, *port)} 48 | 49 | pr := *prefix 50 | if !strings.HasPrefix(pr, "/") { 51 | pr = "/" + pr 52 | } 53 | 54 | config := common.Config{EtcdEndpoints: endpoints, 55 | EtcdPrefix: pr, 56 | InitialTopologyFile: topologyFile, 57 | } 58 | svcInfo, err := common.InitializeService(romanad, config) 59 | if err != nil { 60 | log.Error(err) 61 | os.Exit(3) 62 | } 63 | if svcInfo != nil { 64 | for { 65 | msg := <-svcInfo.Channel 66 | log.Info(msg) 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /cni/cnilink.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package cni 17 | 18 | import ( 19 | "fmt" 20 | "net" 21 | 22 | "github.com/containernetworking/cni/pkg/ns" 23 | "github.com/vishvananda/netlink" 24 | ) 25 | 26 | // 27 | // TODO 28 | // Warning! 29 | // Functions in this file are ported over from containernetworking/cni/ip/link.go 30 | // for a reason that we can't use ip.SetupVeth with random hostVeth name. 31 | // 32 | 33 | // SetupVeth sets up a pair of virtual ethernet devices. 34 | // Call SetupVeth from inside the container netns. It will create both veth 35 | // devices and move the host-side veth into the provided hostNS namespace. 36 | // On success, SetupVeth returns (hostVeth, containerVeth, nil) 37 | func SetupVeth(contVethName, hostVethName string, mtu int, hostNS ns.NetNS) (net.Interface, net.Interface, error) { 38 | contVeth, err := makeVethPair(contVethName, hostVethName, mtu) 39 | if err != nil { 40 | return net.Interface{}, net.Interface{}, err 41 | } 42 | 43 | if err = netlink.LinkSetUp(contVeth); err != nil { 44 | return net.Interface{}, net.Interface{}, fmt.Errorf("failed to set %q up: %v", contVethName, err) 45 | } 46 | 47 | hostVeth, err := netlink.LinkByName(hostVethName) 48 | if err != nil { 49 | return net.Interface{}, net.Interface{}, fmt.Errorf("failed to lookup %q: %v", hostVethName, err) 50 | } 51 | 52 | if err = netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd())); err != nil { 53 | return net.Interface{}, net.Interface{}, fmt.Errorf("failed to move veth to host netns: %v", err) 54 | } 55 | 56 | err = hostNS.Do(func(_ ns.NetNS) error { 57 | hostVeth, err = netlink.LinkByName(hostVethName) 58 | if err != nil { 59 | return fmt.Errorf("failed to lookup %q in %q: %v", hostVethName, hostNS.Path(), err) 60 | } 61 | 62 | if err = netlink.LinkSetUp(hostVeth); err != nil { 63 | return fmt.Errorf("failed to set %q up: %v", hostVethName, err) 64 | } 65 | return nil 66 | }) 67 | if err != nil { 68 | return net.Interface{}, net.Interface{}, err 69 | } 70 | return ifaceFromNetlinkLink(hostVeth), ifaceFromNetlinkLink(contVeth), nil 71 | } 72 | 73 | func makeVethPair(name, peer string, mtu int) (netlink.Link, error) { 74 | veth := &netlink.Veth{ 75 | LinkAttrs: netlink.LinkAttrs{ 76 | Name: name, 77 | Flags: net.FlagUp, 78 | MTU: mtu, 79 | }, 80 | PeerName: peer, 81 | } 82 | if err := netlink.LinkAdd(veth); err != nil { 83 | return nil, err 84 | } 85 | 86 | return veth, nil 87 | } 88 | 89 | func ifaceFromNetlinkLink(l netlink.Link) net.Interface { 90 | a := l.Attrs() 91 | return net.Interface{ 92 | Index: a.Index, 93 | MTU: a.MTU, 94 | Name: a.Name, 95 | HardwareAddr: a.HardwareAddr, 96 | Flags: a.Flags, 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /cni/divert_rules.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package cni 17 | 18 | import ( 19 | "github.com/romana/core/agent/iptsave" 20 | ) 21 | 22 | func MakeDivertRules(nodename string, op iptsave.RenderState) []*iptsave.IPchain { 23 | return []*iptsave.IPchain{ 24 | &iptsave.IPchain{ 25 | Name: "INPUT", 26 | Policy: "-", 27 | Rules: []*iptsave.IPrule{ 28 | &iptsave.IPrule{ 29 | RenderState: op, 30 | Match: []*iptsave.Match{ 31 | &iptsave.Match{ 32 | Body: "-i " + nodename, 33 | }, 34 | }, 35 | Action: iptsave.IPtablesAction{ 36 | Type: iptsave.ActionDefault, 37 | Body: "ROMANA-INPUT", 38 | }, 39 | }, 40 | }, 41 | }, 42 | &iptsave.IPchain{ 43 | Name: "FORWARD", 44 | Policy: "-", 45 | Rules: []*iptsave.IPrule{ 46 | &iptsave.IPrule{ 47 | RenderState: op, 48 | Match: []*iptsave.Match{ 49 | &iptsave.Match{ 50 | Body: "-i " + nodename, 51 | }, 52 | }, 53 | Action: iptsave.IPtablesAction{ 54 | Type: iptsave.ActionDefault, 55 | Body: "ROMANA-FORWARD-OUT", 56 | }, 57 | }, 58 | }, 59 | }, 60 | &iptsave.IPchain{ 61 | Name: "FORWARD", 62 | Policy: "-", 63 | Rules: []*iptsave.IPrule{ 64 | &iptsave.IPrule{ 65 | RenderState: op, 66 | Match: []*iptsave.Match{ 67 | &iptsave.Match{ 68 | Body: "-o " + nodename, 69 | }, 70 | }, 71 | Action: iptsave.IPtablesAction{ 72 | Type: iptsave.ActionDefault, 73 | Body: "ROMANA-FORWARD-IN", 74 | }, 75 | }, 76 | }, 77 | }, 78 | &iptsave.IPchain{ 79 | Name: "OUTPUT", 80 | Policy: "-", 81 | Rules: []*iptsave.IPrule{ 82 | &iptsave.IPrule{ 83 | RenderState: op, 84 | Match: []*iptsave.Match{ 85 | &iptsave.Match{ 86 | Body: "-o " + nodename, 87 | }, 88 | }, 89 | Action: iptsave.IPtablesAction{ 90 | Type: iptsave.ActionDefault, 91 | Body: "ROMANA-OUTPUT", 92 | }, 93 | }, 94 | }, 95 | }, 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /cni/kubernetes.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package cni 17 | 18 | import ( 19 | "fmt" 20 | "net" 21 | 22 | "github.com/containernetworking/cni/pkg/types" 23 | "k8s.io/client-go/kubernetes" 24 | "k8s.io/client-go/tools/clientcmd" 25 | ) 26 | 27 | type PodDescription struct { 28 | Name string 29 | Namespace string 30 | Labels map[string]string 31 | Annotations map[string]string 32 | } 33 | 34 | // K8sArgs is the valid CNI_ARGS used for Kubernetes. 35 | type K8sArgs struct { 36 | types.CommonArgs 37 | IP net.IP 38 | K8S_POD_NAME types.UnmarshallableString 39 | K8S_POD_NAMESPACE types.UnmarshallableString 40 | K8S_POD_INFRA_CONTAINER_ID types.UnmarshallableString 41 | } 42 | 43 | // MakeVethName generates veth name that can be used for external part 44 | // of the veth interface. 45 | func (k8s K8sArgs) MakeVethName() string { 46 | const suffixLength = 8 47 | const vethPrefix = "romana" 48 | var suffix string 49 | infra := string(k8s.K8S_POD_INFRA_CONTAINER_ID) 50 | if len(infra) > suffixLength { 51 | suffix = infra[:suffixLength] 52 | } else { 53 | suffix = infra 54 | } 55 | 56 | return fmt.Sprintf("%s-%s", vethPrefix, suffix) 57 | } 58 | 59 | // MakePodName returns unique pod name. 60 | func (k8s K8sArgs) MakePodName() string { 61 | const suffixLength = 8 62 | var suffix string 63 | infra := string(k8s.K8S_POD_INFRA_CONTAINER_ID) 64 | if len(infra) > suffixLength { 65 | suffix = infra[:suffixLength] 66 | } else { 67 | suffix = infra 68 | } 69 | 70 | return fmt.Sprintf("%s.%s.%s", k8s.K8S_POD_NAME, k8s.K8S_POD_NAMESPACE, suffix) 71 | } 72 | 73 | // GetPodDescription retrieves additional information about pod that being created 74 | // or deleted using CNI. 75 | func GetPodDescription(args K8sArgs, configFile string) (*PodDescription, error) { 76 | // Init kubernetes client. Attempt to load from statically configured k8s config or fallback on in-cluster 77 | kubeClientConfig, err := clientcmd.BuildConfigFromFlags("", configFile) 78 | if err != nil { 79 | return nil, err 80 | } 81 | kubeClient, err := kubernetes.NewForConfig(kubeClientConfig) 82 | if err != nil { 83 | return nil, err 84 | } 85 | 86 | pod, err := kubeClient.Core().Pods(string(args.K8S_POD_NAMESPACE)).Get(fmt.Sprintf("%s", args.K8S_POD_NAME)) 87 | if err != nil { 88 | return nil, fmt.Errorf("Failed to discover a pod %s, err=(%s)", args.K8S_POD_NAME, err) 89 | } 90 | 91 | res := PodDescription{ 92 | Name: args.MakePodName(), 93 | Namespace: string(args.K8S_POD_NAMESPACE), 94 | Labels: pod.Labels, 95 | Annotations: pod.Annotations, 96 | } 97 | 98 | return &res, nil 99 | } 100 | -------------------------------------------------------------------------------- /cni/netconfig.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package cni 17 | 18 | import ( 19 | "database/sql/driver" 20 | "encoding/json" 21 | "fmt" 22 | "net" 23 | ) 24 | 25 | // NetworkRequest specifies messages sent to the 26 | // agent containing information on how to configure network 27 | // on its host. 28 | type NetworkRequest struct { 29 | NetIf NetIf `json:"net_if,omitempty"` 30 | // TODO we should not need this tag 31 | Options map[string]string `json:"options,omitempty"` 32 | } 33 | 34 | // NetIf is a structure that represents 35 | // network interface and its IP configuration 36 | // together with basic methods operating on this structure. 37 | type NetIf struct { 38 | Name string `form:"interface_name" sql:"unique"` 39 | Mac string `form:"mac_address" gorm:"primary_key"` 40 | IP IP `form:"ip_address" sql:"TYPE:varchar"` 41 | } 42 | 43 | // MarshalJSON properly marshals NetIf structure. 44 | func (n NetIf) MarshalJSON() ([]byte, error) { 45 | m := make(map[string]string) 46 | m["interface_name"] = n.Name 47 | m["mac_address"] = n.Mac 48 | m["ip_address"] = n.IP.String() 49 | return json.Marshal(m) 50 | } 51 | 52 | // GetName implements firewall.FirewallEndpoint 53 | func (n NetIf) GetName() string { 54 | return n.Name 55 | } 56 | 57 | // GetMac implements firewall.FirewallEndpoint 58 | func (n NetIf) GetMac() string { 59 | return n.Mac 60 | } 61 | 62 | // GetIP implements firewall.FirewallEndpoint 63 | func (n NetIf) GetIP() net.IP { 64 | return n.IP.IP 65 | } 66 | 67 | // SetIP parses and sets the IP address of the interface. 68 | func (n *NetIf) SetIP(ip string) error { 69 | n.IP.IP = net.ParseIP(ip) 70 | if n.IP.IP == nil && ip != "" { 71 | return fmt.Errorf("failed to parse Netif, bad IP: %s", ip) 72 | } 73 | 74 | return nil 75 | } 76 | 77 | // UnmarshalJSON results in having NetIf implement Unmarshaler 78 | // interface from encoding/json. This is needed because we use 79 | // a type like net.IP here, not a simple type, and so a call to 80 | // net.ParseIP is required to unmarshal this properly. 81 | func (n *NetIf) UnmarshalJSON(data []byte) error { 82 | m := make(map[string]string) 83 | json.Unmarshal(data, &m) 84 | ip := m["ip_address"] 85 | n.IP.IP = net.ParseIP(ip) 86 | if n.IP.IP == nil && ip != "" { 87 | return fmt.Errorf("failed to parse Netif, bad IP: %s", ip) 88 | } 89 | 90 | n.Name = m["interface_name"] 91 | n.Mac = m["mac_address"] 92 | return nil 93 | } 94 | 95 | // IP structure is basically net.IP, but we redefine it so we 96 | // can implement Valuer and Scanner interfaces on it for storage. 97 | type IP struct { 98 | net.IP 99 | } 100 | 101 | // Value implements driver.Valuer interface on IP 102 | func (i IP) Value() (driver.Value, error) { 103 | return driver.Value(i.String()), nil 104 | } 105 | 106 | // Scan implements driver.Scanner interface on IP 107 | func (i *IP) Scan(src interface{}) error { 108 | switch src := src.(type) { 109 | case string: 110 | ip := net.ParseIP(src) 111 | if ip == nil { 112 | return fmt.Errorf("cannot parse IP %s", src) 113 | } 114 | i.IP = ip 115 | return nil 116 | case []uint8: 117 | i.IP = net.ParseIP(string(src)) 118 | return nil 119 | default: 120 | return fmt.Errorf("incompatible type for IP %s", src) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /cni/plugin_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package cni 17 | 18 | import ( 19 | "fmt" 20 | "net" 21 | "strings" 22 | "testing" 23 | 24 | "github.com/vishvananda/netlink" 25 | "golang.org/x/sys/unix" 26 | ) 27 | 28 | type mockNlRouteHandle struct { 29 | linkByNameErr error 30 | addRouteErr error 31 | replaceRouteErr error 32 | } 33 | 34 | func (m mockNlRouteHandle) LinkByName(name string) (netlink.Link, error) { 35 | return &netlink.Device{}, m.linkByNameErr 36 | } 37 | 38 | func (m mockNlRouteHandle) RouteAdd(*netlink.Route) error { 39 | return m.addRouteErr 40 | } 41 | 42 | func (m mockNlRouteHandle) RouteReplace(*netlink.Route) error { 43 | return m.replaceRouteErr 44 | } 45 | 46 | func (m mockNlRouteHandle) Delete() {} 47 | 48 | func (m mockNlRouteHandle) RouteGet(net.IP) ([]netlink.Route, error) { 49 | return []netlink.Route{}, nil 50 | } 51 | 52 | func TestAddEndpointRoute(t *testing.T) { 53 | dummyIpnet := &net.IPNet{IP: net.ParseIP("127.0.0.1"), Mask: net.IPMask([]byte{0xff, 0xff, 0xff, 0xff})} 54 | 55 | cases := []struct { 56 | name string 57 | ifaceName string 58 | ip *net.IPNet 59 | msg string 60 | mock mockNlRouteHandle 61 | test func(error) bool 62 | }{ 63 | { 64 | "detect failure during LinkByName", 65 | "dummy", 66 | dummyIpnet, 67 | "", 68 | mockNlRouteHandle{linkByNameErr: fmt.Errorf("bang")}, 69 | func(err error) bool { return err.Error() == "bang" }, 70 | }, 71 | { 72 | "detect failure during route add", 73 | "dummy", 74 | dummyIpnet, 75 | "", 76 | mockNlRouteHandle{addRouteErr: fmt.Errorf("bang")}, 77 | func(err error) bool { return strings.Contains(err.Error(), "couldn't create route") }, 78 | }, 79 | { 80 | "check switch to ReplaceRoute when unix.EEXIST", 81 | "dummy", 82 | dummyIpnet, 83 | "", 84 | mockNlRouteHandle{ 85 | addRouteErr: unix.EEXIST, 86 | replaceRouteErr: fmt.Errorf("bang"), 87 | }, 88 | func(err error) bool { return strings.Contains(err.Error(), "couldn't replace route") }, 89 | }, 90 | { 91 | "check RouteAdd success", 92 | "dummy", 93 | dummyIpnet, 94 | "", 95 | mockNlRouteHandle{}, 96 | func(err error) bool { return err == nil }, 97 | }, 98 | { 99 | "check switch to ReplaceRoute success", 100 | "dummy", 101 | dummyIpnet, 102 | "", 103 | mockNlRouteHandle{ 104 | addRouteErr: unix.EEXIST, 105 | }, 106 | func(err error) bool { return err == nil }, 107 | }, 108 | } 109 | 110 | for _, tc := range cases { 111 | t.Run(tc.name, func(t *testing.T) { 112 | err := AddEndpointRoute(tc.ifaceName, tc.ip, tc.mock) 113 | if !tc.test(err) { 114 | t.Fatalf("%s, %s", tc.msg, err) 115 | } 116 | }) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /cni/policy.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package cni 17 | 18 | import ( 19 | "fmt" 20 | "os/exec" 21 | "strings" 22 | 23 | "github.com/romana/core/agent/iptsave" 24 | "github.com/romana/rlog" 25 | ) 26 | 27 | func enablePodPolicy(ifaceName string) error { 28 | return manageDivertRules(MakeDivertRules(ifaceName, iptsave.RenderAppendRule)) 29 | } 30 | 31 | func disablePodPolicy(ifaceName string) error { 32 | return manageDivertRules(MakeDivertRules(ifaceName, iptsave.RenderDeleteRule)) 33 | } 34 | 35 | func manageDivertRules(divertRules []*iptsave.IPchain) error { 36 | IptablesBin, err := exec.LookPath("iptables") 37 | if err != nil { 38 | return err 39 | } 40 | 41 | var rules string 42 | for _, chain := range divertRules { 43 | rules += chain.RenderFooter() 44 | } 45 | 46 | makeArgs := func(a []string, b ...string) []string { 47 | var result []string 48 | result = append(b, a...) 49 | return result 50 | } 51 | 52 | for _, rule := range strings.Split(rules, "\n") { 53 | if rule == "" { 54 | continue 55 | } 56 | rlog.Debugf("EXEC %s", makeArgs(strings.Split(rule, " ")), IptablesBin, "-t", "filter") 57 | data, err := exec.Command(IptablesBin, makeArgs(strings.Split(rule, " "), "-t", "filter")...).CombinedOutput() 58 | if err != nil { 59 | return fmt.Errorf("%s, err=%s", data, err) 60 | } 61 | } 62 | 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /common/api/errors/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package errors 17 | 18 | import ( 19 | "fmt" 20 | "strings" 21 | ) 22 | 23 | // RomanaNotFoundError represents an error when an entity (or resource) 24 | // is not found. It is a separate error because clients may wish to check for this 25 | // error. 26 | type RomanaNotFoundError struct { 27 | // Attributes represent key-value pairs used to search 28 | // for the object. 29 | Attributes map[string]string 30 | Type string 31 | Message string 32 | } 33 | 34 | // NewRomanaNotFoundError creates a RomanaNotFoundError. Each element 35 | // of attrs is interpreted as a "key=value" pair. 36 | func NewRomanaNotFoundError(message string, t string, attrs ...string) RomanaNotFoundError { 37 | attrMap := make(map[string]string) 38 | for _, attr := range attrs { 39 | kv := strings.SplitN(attr, "=", 2) 40 | k := kv[0] 41 | v := kv[1] 42 | attrMap[k] = v 43 | } 44 | err := RomanaNotFoundError{Message: message, 45 | Type: t, 46 | Attributes: attrMap, 47 | } 48 | return err 49 | } 50 | 51 | func (rnfe RomanaNotFoundError) Error() string { 52 | if rnfe.Message == "" { 53 | return fmt.Sprintf("An %s object with attributes %v not found", rnfe.Type, rnfe.Attributes) 54 | } else { 55 | return rnfe.Message 56 | } 57 | } 58 | 59 | // RomanaExistsError represents an error when an entity already 60 | // exists. 61 | type RomanaExistsError struct { 62 | Type string 63 | // Attributes represent key-value pairs used to add 64 | // the object. 65 | Attributes map[string]string 66 | Object interface{} 67 | Message string 68 | } 69 | 70 | func NewRomanaExistsError(obj interface{}, t string, attrs ...string) RomanaExistsError { 71 | attrMap := make(map[string]string) 72 | for _, attr := range attrs { 73 | kv := strings.SplitN(attr, "=", 2) 74 | k := kv[0] 75 | v := kv[1] 76 | attrMap[k] = v 77 | } 78 | err := RomanaExistsError{ 79 | Type: t, 80 | Object: obj, 81 | Attributes: attrMap, 82 | } 83 | return err 84 | } 85 | 86 | func NewRomanaExistsErrorWithMessage(msg string, obj interface{}, t string, attrs ...string) RomanaExistsError { 87 | err := NewRomanaExistsError(obj, t, attrs...) 88 | err.Message = msg 89 | return err 90 | } 91 | 92 | func (ree RomanaExistsError) Error() string { 93 | if ree.Message == "" { 94 | return fmt.Sprintf("A[n] '%s' object identified by %+v already exists: %s", ree.Type, ree.Attributes, ree.Object) 95 | } else { 96 | return ree.Message 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /common/api/errors/helpers.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package errors 17 | 18 | import ( 19 | "fmt" 20 | 21 | "github.com/romana/core/common" 22 | ) 23 | 24 | // romanaErrorToHTTPError is a helper method that creates an 25 | // HTTP error (one that the middleware automatically converts to the right 26 | // HTTP status code and response) from the provided Romana error, if possible. 27 | // If the provided is not a Romana error, or if no corresponding HTTP errror 28 | // can be provided, the original error is returned. Thus the signature takes a 29 | // generic error and also returns it. 30 | func RomanaErrorToHTTPError(err error) error { 31 | if err == nil { 32 | return nil 33 | } 34 | switch err := err.(type) { 35 | case RomanaNotFoundError: 36 | return common.NewError404(err.Type, fmt.Sprintf("%v", err.Attributes)) 37 | case RomanaExistsError: 38 | common.NewErrorConflict(err) 39 | 40 | } 41 | return err 42 | } 43 | -------------------------------------------------------------------------------- /common/api/policy.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package api 17 | 18 | import ( 19 | "fmt" 20 | 21 | "github.com/romana/core/common" 22 | ) 23 | 24 | const ( 25 | // Max port number for TCP/UDP. 26 | MaxPortNumber = 65535 27 | MaxIcmpType = 255 28 | 29 | // Wildcard 30 | Wildcard = "any" 31 | ) 32 | 33 | // Endpoint represents an endpoint - that is, something that 34 | // has an IP address and routes to/from. It can be a container, 35 | // a Kubernetes POD, a VM, etc. 36 | type Endpoint struct { 37 | Peer string `json:"peer,omitempty"` 38 | Cidr string `json:"cidr,omitempty"` 39 | Dest string `json:"dest,omitempty"` 40 | TenantID string `json:"tenant_id,omitempty"` 41 | SegmentID string `json:"segment_id,omitempty"` 42 | } 43 | 44 | func (e Endpoint) String() string { 45 | return common.String(e) 46 | } 47 | 48 | const ( 49 | PolicyDirectionIngress = "ingress" 50 | PolicyDirectionEgress = "egress" 51 | ) 52 | 53 | type PortRange [2]uint 54 | 55 | func (p PortRange) String() string { 56 | return fmt.Sprintf("%d-%d", p[0], p[1]) 57 | } 58 | 59 | // Rule describes a rule of the policy. The following requirements apply 60 | // (the policy would not be validated otherwise): 61 | // 1. Protocol must be specified. 62 | // 2. Protocol must be one of those validated by isValidProto(). 63 | // 3. Ports cannot be negative or greater than 65535. 64 | // 4. If Protocol specified is "icmp", Ports and PortRanges fields should be blank. 65 | // 5. If Protocol specified is not "icmp", Icmptype and IcmpCode should be unspecified. 66 | type Rule struct { 67 | Protocol string `json:"protocol,omitempty"` 68 | Ports []uint `json:"ports,omitempty"` 69 | PortRanges []PortRange `json:"port_ranges,omitempty"` 70 | // IcmpType only applies if Protocol value is ICMP and 71 | // is mutually exclusive with Ports or PortRanges 72 | IcmpType uint `json:"icmp_type,omitempty"` 73 | IcmpCode uint `json:"icmp_code,omitempty"` 74 | IsStateful bool `json:"is_stateful,omitempty"` 75 | } 76 | 77 | func (r Rule) String() string { 78 | return common.String(r) 79 | } 80 | 81 | type Rules []Rule 82 | 83 | // Metadata attached to entities for various external environments like Open Stack / Kubernetes 84 | type Tag struct { 85 | Key string `json:"key,omitempty"` 86 | Value string `json:"value,omitempty"` 87 | } 88 | 89 | // Policy describes Romana network security policy. 90 | // For examples, see: 91 | // 1. https://github.com/romana/core/blob/master/policy/policy.sample.json 92 | // 2. https://github.com/romana/core/blob/master/policy/policy.example.agent.json 93 | type Policy struct { 94 | ID string `json:"id"` 95 | // Direction is one of common.PolicyDirectionIngress or common.PolicyDirectionIngress, 96 | // otherwise common.Validate will return an error. 97 | Direction string `json:"direction,omitempty" romana:"desc:Direction is one of 'ingress' or egress'."` 98 | // Description is human-redable description of the policy. 99 | Description string `json:"description,omitempty"` 100 | // Datacenter describes a Romana deployment. 101 | AppliedTo []Endpoint `json:"applied_to,omitempty"` 102 | Ingress []RomanaIngress `json:"ingress,omitempty"` 103 | // Tags []Tag `json:"tags,omitempty"` 104 | } 105 | 106 | type RomanaIngress struct { 107 | Peers []Endpoint `json:"peers,omitempty"` 108 | Rules []Rule `json:"rules,omitempty"` 109 | } 110 | 111 | func (p Policy) String() string { 112 | return common.String(p) 113 | } 114 | -------------------------------------------------------------------------------- /common/api/romanavip.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package api 17 | 18 | type RomanaVIP struct { 19 | Auto bool `json:"auto"` 20 | IP string `json:"ip"` 21 | } 22 | 23 | type ExposedIPSpec struct { 24 | RomanaVIP RomanaVIP 25 | NodeIPAddress string 26 | Activated bool 27 | Namespace string 28 | } 29 | -------------------------------------------------------------------------------- /common/buildinfo.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package common 17 | 18 | // Build information support. 19 | 20 | import ( 21 | "fmt" 22 | ) 23 | 24 | // Build Information and Timestamp. 25 | // Pass build information to the executable using go run as below: 26 | // 27 | // go run -ldflags \ 28 | // "-X github.com/romana/core/common.buildInfo=`git describe --always` \ 29 | // -X github.com/romana/core/common.buildTimeStamp=`date -u '+%Y-%m-%d_%I:%M:%S%p'`" \ 30 | // main.go -version 31 | // 32 | // or using go build as below: 33 | // 34 | // go build -ldflags \ 35 | // "-X github.com/romana/core/common.buildInfo=`git describe --always` \ 36 | // -X github.com/romana/core/common.buildTimeStamp=`date -u '+%Y-%m-%d_%I:%M:%S%p'`" \ 37 | // main.go 38 | // 39 | var buildInfo = "No Build Information Provided" 40 | var buildTimeStamp = "No Build Time Provided" 41 | 42 | // BuildInfo return build revision and time string. 43 | func BuildInfo() string { 44 | return fmt.Sprintf("Build Revision: %s\nBuild Time: %s", buildInfo, buildTimeStamp) 45 | } 46 | -------------------------------------------------------------------------------- /common/client/idring/merge.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package idring 17 | 18 | import "sort" 19 | 20 | type rangeSorter []Range 21 | 22 | func (a rangeSorter) Len() int { return len(a) } 23 | func (a rangeSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 24 | func (a rangeSorter) Less(i, j int) bool { return a[i].Min < a[j].Min } 25 | 26 | // Merge a slice of ranges combining adjacent ranges together 27 | // and skipping included ranges. 28 | func Merge(ranges []Range) []Range { 29 | if len(ranges) < 2 { 30 | return ranges 31 | } 32 | 33 | var result []Range 34 | sort.Sort(rangeSorter(ranges)) 35 | for i, _ := range ranges { 36 | if len(result) == 0 { 37 | result = append(result, ranges[i]) 38 | continue 39 | } 40 | 41 | resultLastIndex := len(result) - 1 42 | lastResult := result[resultLastIndex] 43 | 44 | // skip if current element fully included 45 | // in last result element 46 | if lastResult.Min <= ranges[i].Min && lastResult.Max >= ranges[i].Max { 47 | continue 48 | } 49 | 50 | // merge 51 | if lastResult.Max+1 >= ranges[i].Min { 52 | result[resultLastIndex] = Range{lastResult.Min, ranges[i].Max} 53 | continue 54 | } 55 | 56 | // can't merge, add as is 57 | result = append(result, ranges[i]) 58 | } 59 | 60 | return result 61 | } 62 | -------------------------------------------------------------------------------- /common/client/idring/merge_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package idring 17 | 18 | import "testing" 19 | 20 | func TestMerge(t *testing.T) { 21 | cases := []struct { 22 | name string 23 | ranges []Range 24 | test func([]Range) bool 25 | }{ 26 | { 27 | name: "Full collapse", 28 | ranges: []Range{Range{Min: 43, Max: 44}, Range{Min: 45, Max: 48}, Range{Min: 40, Max: 43}}, 29 | test: func(r []Range) bool { return r[0].Min == 40 && r[0].Max == 48 }, 30 | }, 31 | { 32 | name: "Merge adjacent", 33 | ranges: []Range{Range{Min: 43, Max: 44}, Range{Min: 45, Max: 48}}, 34 | test: func(r []Range) bool { return r[0].Min == 43 && r[0].Max == 48 }, 35 | }, 36 | { 37 | name: "Merge adjacent2", 38 | ranges: []Range{Range{Min: 43, Max: 45}, Range{Min: 45, Max: 48}}, 39 | test: func(r []Range) bool { return r[0].Min == 43 && r[0].Max == 48 }, 40 | }, 41 | { 42 | name: "Merge overlap", 43 | ranges: []Range{Range{Min: 43, Max: 47}, Range{Min: 45, Max: 48}}, 44 | test: func(r []Range) bool { return r[0].Min == 43 && r[0].Max == 48 }, 45 | }, 46 | { 47 | name: "Skip included", 48 | ranges: []Range{Range{Min: 45, Max: 47}, Range{Min: 40, Max: 40}, Range{Min: 40, Max: 43}}, 49 | test: func(r []Range) bool { return r[0].Min == 40 && r[1].Max == 47 }, 50 | }, 51 | { 52 | name: "Dont merge unmergable", 53 | ranges: []Range{Range{Min: 45, Max: 47}, Range{Min: 40, Max: 41}}, 54 | test: func(r []Range) bool { return r[0].Min == 40 && r[1].Max == 47 }, 55 | }, 56 | } 57 | 58 | for _, tc := range cases { 59 | t.Run(tc.name, func(t *testing.T) { 60 | r := Merge(tc.ranges) 61 | t.Logf("Name %s, result %+v", tc.name, r) 62 | if !tc.test(r) { 63 | t.Fatal(tc.name) 64 | } 65 | }) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /common/client/testdata/Test32_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"net1", 5 | "cidr":"10.0.0.0/24", 6 | "block_mask":32 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "net1" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"foo", 17 | "groups":[{ 18 | "name":"host1", 19 | "ip":"192.168.0.1" 20 | }] 21 | } 22 | ] 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /common/client/testdata/Test32_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"net1", 5 | "cidr":"10.0.0.0/32", 6 | "block_mask":32 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "net1" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"foo", 17 | "groups": [{ 18 | "name":"host1", 19 | "ip":"192.168.0.1" 20 | }] 21 | } 22 | ] 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /common/client/testdata/TestBlackout.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"net1", 5 | "cidr":"10.0.0.0/30", 6 | "block_mask":30 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "net1" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"foo", 17 | "groups":[{ 18 | "name":"host1", 19 | "ip":"192.168.0.1" 20 | }] 21 | } 22 | ] 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /common/client/testdata/TestBlockReuseMask30.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"net1", 5 | "cidr":"10.0.0.0/8", 6 | "block_mask":30 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "net1" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"foo", 17 | "groups":[{ 18 | "name":"host1", 19 | "ip":"192.168.0.1" 20 | }] 21 | } 22 | ] 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /common/client/testdata/TestBlockReuseMask32.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"net1", 5 | "cidr":"10.0.0.0/31", 6 | "block_mask":32 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "net1" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"foo", 17 | "groups":[{ 18 | "name":"host1", 19 | "ip":"192.168.0.1" 20 | }] 21 | } 22 | ] 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /common/client/testdata/TestHostAdd713.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks": [ 3 | { 4 | "name": "k8-rack-2a", 5 | "cidr": "10.28.0.0/19", 6 | "block_mask": 24 7 | }, 8 | { 9 | "name": "k8-rack-2b", 10 | "cidr": "10.28.32.0/19", 11 | "block_mask": 24 12 | }, 13 | { 14 | "name": "k8-rack-3a", 15 | "cidr": "10.28.64.0/19", 16 | "block_mask": 24 17 | }, 18 | { 19 | "name": "k8-rack-3b", 20 | "cidr": "10.28.96.0/19", 21 | "block_mask": 24 22 | }, 23 | { 24 | "name": "k8-rack-4a", 25 | "cidr": "10.28.128.0/19", 26 | "block_mask": 24 27 | }, 28 | { 29 | "name": "k8-rack-4b", 30 | "cidr": "10.28.160.0/19", 31 | "block_mask": 24 32 | }, 33 | { 34 | "name": "k8-rack-5a", 35 | "cidr": "10.28.192.0/19", 36 | "block_mask": 24 37 | }, 38 | { 39 | "name": "k8-rack-5b", 40 | "cidr": "10.28.224.0/19", 41 | "block_mask": 24 42 | } 43 | ], 44 | "topologies": [ 45 | { 46 | "networks": [ 47 | "k8-rack-2a" 48 | ], 49 | "map": [ 50 | { 51 | "groups": [ 52 | { 53 | "name": "rack-2a", 54 | "groups": [], 55 | "assignment": { 56 | "rack": "rack-2a" 57 | } 58 | } 59 | ] 60 | } 61 | ] 62 | }, 63 | { 64 | "networks": [ 65 | "k8-rack-2b" 66 | ], 67 | "map": [ 68 | { 69 | "groups": [ 70 | { 71 | "name": "rack-2b", 72 | "groups": [], 73 | "assignment": { 74 | "rack": "rack-2b" 75 | } 76 | } 77 | ] 78 | } 79 | ] 80 | }, 81 | { 82 | "networks": [ 83 | "k8-rack-3a" 84 | ], 85 | "map": [ 86 | { 87 | "groups": [ 88 | { 89 | "name": "rack-3a", 90 | "groups": [], 91 | "assignment": { 92 | "rack": "rack-3a" 93 | } 94 | } 95 | ] 96 | } 97 | ] 98 | }, 99 | { 100 | "networks": [ 101 | "k8-rack-3b" 102 | ], 103 | "map": [ 104 | { 105 | "groups": [ 106 | { 107 | "name": "rack-3b", 108 | "groups": [], 109 | "assignment": { 110 | "rack": "rack-3b" 111 | } 112 | } 113 | ] 114 | } 115 | ] 116 | }, 117 | { 118 | "networks": [ 119 | "k8-rack-4a" 120 | ], 121 | "map": [ 122 | { 123 | "groups": [ 124 | { 125 | "name": "rack-4a", 126 | "groups": [], 127 | "assignment": { 128 | "rack": "rack-4a" 129 | } 130 | } 131 | ] 132 | } 133 | ] 134 | }, 135 | { 136 | "networks": [ 137 | "k8-rack-4b" 138 | ], 139 | "map": [ 140 | { 141 | "groups": [ 142 | { 143 | "name": "rack-4b", 144 | "groups": [], 145 | "assignment": { 146 | "rack": "rack-4b" 147 | } 148 | } 149 | ] 150 | } 151 | ] 152 | }, 153 | { 154 | "networks": [ 155 | "k8-rack-5a" 156 | ], 157 | "map": [ 158 | { 159 | "groups": [ 160 | { 161 | "name": "rack-5a", 162 | "groups": [], 163 | "assignment": { 164 | "rack": "rack-5a" 165 | } 166 | } 167 | ] 168 | } 169 | ] 170 | }, 171 | { 172 | "networks": [ 173 | "k8-rack-5b" 174 | ], 175 | "map": [ 176 | { 177 | "groups": [ 178 | { 179 | "name": "rack-5b", 180 | "groups": [], 181 | "assignment": { 182 | "rack": "rack-5b" 183 | } 184 | } 185 | ] 186 | } 187 | ] 188 | } 189 | ] 190 | } -------------------------------------------------------------------------------- /common/client/testdata/TestHostAdditionSimple.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks": [ 3 | { 4 | "name": "net1", 5 | "cidr": "10.0.0.0/16", 6 | "block_mask": 30 7 | }, 8 | { 9 | "name": "net2", 10 | "cidr": "10.20.0.0/16", 11 | "block_mask": 30 12 | } 13 | ], 14 | "topologies": [ 15 | { 16 | "networks": [ 17 | "net1", "net2" 18 | ], 19 | "map": [ 20 | { 21 | "groups": [ 22 | 23 | ], 24 | "name": "group1" 25 | }, 26 | { 27 | "groups": [ 28 | 29 | ], 30 | "name": "group2" 31 | } 32 | ] 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /common/client/testdata/TestHostAdditionTags.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks": [ 3 | { 4 | "name": "net1", 5 | "cidr": "10.0.0.0/16", 6 | "block_mask": 30 7 | } 8 | ], 9 | "topologies": [ 10 | { 11 | "networks": [ 12 | "net1" 13 | ], 14 | "map": [ 15 | { 16 | "assignment" : { "tier" : "backend" }, 17 | "groups": [ 18 | 19 | ], 20 | "name": "group1" 21 | }, 22 | { 23 | "assignment" : { "tier" : "frontend" }, 24 | "groups": [ 25 | 26 | ], 27 | "name": "group2" 28 | }, 29 | { 30 | "assignment" : { "tier" : "backend" }, 31 | "groups": [ 32 | 33 | ], 34 | "name": "group3" 35 | }, 36 | { 37 | "assignment" : { "tier" : "frontend" }, 38 | "groups": [ 39 | 40 | ], 41 | "name": "group4" 42 | } 43 | ] 44 | } 45 | ] 46 | } -------------------------------------------------------------------------------- /common/client/testdata/TestHostAllocation.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"net1", 5 | "cidr":"10.0.0.0/8", 6 | "block_mask":30 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "net1" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"test", 17 | "groups":[ 18 | { 19 | "name":"ip-192-168-99-10", 20 | "ip":"192.168.99.10" 21 | }, 22 | { 23 | "name":"ip-192-168-99-11", 24 | "ip":"192.168.99.11" 25 | } 26 | ] 27 | } 28 | ] 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /common/client/testdata/TestIPAM_DeallocateIP.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"net1", 5 | "cidr":"10.0.0.0/31", 6 | "block_mask":31 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "net1" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"foo", 17 | "groups":[{ 18 | "name":"host1", 19 | "ip":"192.168.0.1" 20 | }] 21 | } 22 | ] 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /common/client/testdata/TestIPReuse.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"net1", 5 | "cidr":"10.0.0.0/31", 6 | "block_mask":31 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "net1" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"foo", 17 | "groups":[{ 18 | "name":"host1", 19 | "ip":"192.168.0.1" 20 | }] 21 | } 22 | ] 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /common/client/testdata/TestLabelUpdate.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks": [ 3 | { 4 | "name": "net1", 5 | "cidr": "100.11.0.0/20", 6 | "block_mask": 30 7 | } 8 | ], 9 | "topologies": [ 10 | { 11 | "networks": [ "net1" ], 12 | "map": [ 13 | { "name" : "g1", "assignment" : { "rack" : "rack1" }, "groups": [ ] } 14 | ] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /common/client/testdata/TestListBlocks.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"net1", 5 | "cidr":"10.0.0.0/8", 6 | "block_mask":30 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "net1" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"test", 17 | "groups":[ 18 | { 19 | "name":"h1", 20 | "ip":"192.168.99.10" 21 | } 22 | ] 23 | } 24 | ] 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /common/client/testdata/TestMultiNetAllocate.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"net1", 5 | "cidr":"10.0.0.0/8", 6 | "block_mask":30 7 | }, 8 | { 9 | "name":"net2", 10 | "cidr":"11.0.0.0/8", 11 | "block_mask":30 12 | } 13 | ], 14 | "topologies":[ 15 | { 16 | "networks":[ 17 | "net1" 18 | ], 19 | "map":[ 20 | { 21 | "groups":[ 22 | { 23 | "name":"host1", 24 | "ip":"192.168.99.10" 25 | } 26 | ] 27 | } 28 | ] 29 | }, 30 | { 31 | "networks":[ 32 | "net2" 33 | ], 34 | "map":[ 35 | { 36 | "groups":[ 37 | { 38 | "name":"host2", 39 | "ip":"192.168.99.11" 40 | } 41 | ] 42 | } 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /common/client/testdata/TestNodeAssignment.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks": [ 3 | { 4 | "name": "rnet-1", 5 | "cidr": "100.11.0.0/20", 6 | "block_mask": 30 7 | }, 8 | { 9 | "name": "rnet-2", 10 | "cidr": "100.22.0.0/20", 11 | "block_mask": 30 12 | } 13 | ], 14 | "topologies": [ 15 | { 16 | "networks": [ "rnet-1" ], 17 | "map": [ 18 | { "name" : "g1", "assignment" : { "rack" : "rack-1" }, "groups": [ ] } 19 | ] 20 | }, 21 | { 22 | "networks": [ "rnet-2" ], 23 | "map": [ 24 | { "name" : "g2", "assignment" : { "rack" : "rack-2" }, "groups": [ ] }, 25 | { "name" : "g3", "assignment" : { "rack" : "rack-3" }, "groups": [ ] }, 26 | { "name" : "g4", "assignment" : { "rack" : "rack-4" }, "groups": [ ] } 27 | ] 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /common/client/testdata/TestOutOfBoundsError.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"net1", 5 | "cidr":"10.0.0.0/8", 6 | "block_mask":30 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "net1" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"foo", 17 | "groups":[{ 18 | "name":"host1", 19 | "ip":"192.168.0.1" 20 | }] 21 | } 22 | ] 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /common/client/testdata/TestOverlappingCIDRs.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks": [ 3 | { 4 | "name": "rnet-1", 5 | "cidr": "100.11.0.0/20", 6 | "block_mask": 30 7 | }, 8 | { 9 | "name": "rnet-2", 10 | "cidr": "100.11.0.0/20", 11 | "block_mask": 30 12 | } 13 | ], 14 | "topologies": [ 15 | { 16 | "networks": [ "rnet-1", "rnet-2" ], 17 | "map": [ 18 | { "groups": [ ] } 19 | ] 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /common/client/testdata/TestPanic.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks": [ 3 | { 4 | "name": "rnet-1", 5 | "cidr": "100.11.0.0/20", 6 | "block_mask": 30 7 | }, 8 | { 9 | "name": "rnet-2", 10 | "cidr": "100.22.0.0/20", 11 | "block_mask": 30 12 | } 13 | ], 14 | "topologies": [ 15 | { 16 | "networks": [ "rnet-1" ], 17 | "map": [ 18 | { "groups": [ ] } 19 | ] 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /common/client/testdata/TestParseMultiHostGroupsWithPrefix.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"vlanA", 5 | "cidr":"10.1.0.0/16", 6 | "block_mask" : 28 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "vlanA" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"block-host-routes, prefix-announce-bgp:peerxxxx", 17 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" }, { "name" : "h1", "ip" : "1.1.1.1" } ] 18 | }, 19 | { 20 | "routing":"block-host-routes, prefix-announce-bgp:peerxxxx", 21 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" }, { "name" : "h1", "ip" : "1.1.1.1" } ] 22 | }, 23 | { 24 | "routing":"block-host-routes, prefix-announce-bgp:peerxxxx", 25 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" }, { "name" : "h1", "ip" : "1.1.1.1" } ] 26 | }, 27 | { 28 | "routing":"block-host-routes, prefix-announce-bgp:peerxxxx", 29 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" }, { "name" : "h1", "ip" : "1.1.1.1" } ] 30 | } 31 | ] 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /common/client/testdata/TestParsePrefixPerHostA.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"vlanA", 5 | "cidr":"10.1.0.0/16", 6 | "block_mask" : 28 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "vlanA" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"prefix-on-host", 17 | "groups":[ 18 | { "name" : "h1", "ip" : "1.1.1.1" } 19 | ] 20 | }, 21 | { 22 | "routing":"prefix-on-host", 23 | "groups":[ 24 | { "name" : "h1", "ip" : "1.1.1.1" } 25 | ] 26 | }, 27 | { 28 | "routing":"prefix-on-host", 29 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" } ] 30 | }, 31 | { 32 | "routing":"prefix-on-host", 33 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" } ] 34 | } 35 | ] 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /common/client/testdata/TestParsePrefixPerHostB.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"vlanA", 5 | "cidr":"10.1.0.0/16", 6 | "block_mask" : 28 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "vlanA" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"prefix-announce-bgp:peerxxxx", 17 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" } ] 18 | }, 19 | { 20 | "routing":"prefix-announce-bgp:peerxxxx", 21 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" } ] 22 | }, 23 | { 24 | "routing":"prefix-announce-bgp:peerxxxx", 25 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" } ] 26 | }, 27 | { 28 | "routing":"prefix-announce-bgp:peerxxxx", 29 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" } ] 30 | } 31 | ] 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /common/client/testdata/TestParseSimpleFlatNetworkA.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"vlanA", 5 | "cidr":"10.1.0.0/16", 6 | "block_mask": 28 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "vlanA" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"block-on-host", 17 | "groups":[ 18 | { "name" : "h1", "ip" : "1.1.1.1" }, 19 | { "name" : "h1", "ip" : "1.1.1.1" }, 20 | { "name" : "h1", "ip" : "1.1.1.1" }, 21 | { "name" : "h1", "ip" : "1.1.1.1" } 22 | ] 23 | } 24 | ] 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /common/client/testdata/TestParseSimpleFlatNetworkB.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"vlanA", 5 | "cidr":"10.1.0.0/16", 6 | "block_mask": 28 7 | 8 | } 9 | ], 10 | "topologies":[ 11 | { 12 | "networks":[ 13 | "vlanA" 14 | ], 15 | "map":[ 16 | { 17 | "routing":"block-announce-bgp:peerxxxxx", 18 | "groups":[ 19 | { "name" : "h1", "ip" : "1.1.1.1" }, 20 | { "name" : "h2", "ip" : "1.1.1.1" }, 21 | { "name" : "h3", "ip" : "1.1.1.1" }, 22 | { "name" : "h4", "ip" : "1.1.1.1" } 23 | ] 24 | } 25 | ] 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /common/client/testdata/TestParseSimpleFlatNetworkC.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"vlanA", 5 | "cidr":"10.1.0.0/16", 6 | "block_mask" : 28 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "vlanA" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"block-on-host,block - announce - bgp: peerxxxxx", 17 | "groups":[ 18 | { "name" : "h1", "ip" : "1.1.1.1" }, 19 | { "name" : "h1", "ip" : "1.1.1.1" }, 20 | { "name" : "h1", "ip" : "1.1.1.1" }, 21 | { "name" : "h1", "ip" : "1.1.1.1" } 22 | ] 23 | } 24 | ] 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /common/client/testdata/TestParseVPCRoutingForTwoAZs.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"subnetA", 5 | "cidr":"10.1.0.0/16", 6 | "block_mask" : 28 7 | }, 8 | { 9 | "name":"subnetB", 10 | "cidr":"10.2.0.0/16", 11 | "block_mask" : 28 12 | } 13 | ], 14 | "topologies":[ 15 | { 16 | "networks":[ 17 | "subnetA" 18 | ], 19 | "map":[ 20 | { 21 | "routing":"block-host-routes,prefix-announce-vpc", 22 | "groups":[] 23 | }, 24 | { 25 | "routing":"block-host-routes,prefix-announce-vpc", 26 | "groups":[] 27 | } 28 | ] 29 | }, 30 | { 31 | "networks":[ 32 | "subnetB" 33 | ], 34 | "map":[ 35 | { 36 | "routing":"block-host-routes,prefix-announce-vpc", 37 | "groups":[] 38 | }, 39 | { 40 | "routing":"block-host-routes,prefix-announce-vpc", 41 | "groups":[] 42 | } 43 | ] 44 | } 45 | ] 46 | } -------------------------------------------------------------------------------- /common/client/testdata/TestPredefinedHosts.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks": [{ 3 | "name": "net1", 4 | "cidr": "10.0.0.0/8", 5 | "block_mask" : 28 6 | }, 7 | { 8 | "name": "net2", 9 | "cidr": "11.0.0.0/8", 10 | "block_mask" : 28 11 | } 12 | ], 13 | "topologies": [{ 14 | "networks": ["net1"], 15 | "map": [{ 16 | "cidr": "10.0.0.0/8", 17 | "groups": [{ 18 | "cidr": "10.0.0.0/9", 19 | "groups": [ 20 | { 21 | "name" : "h1", 22 | "ip": "192.168.99.10" 23 | }, 24 | { 25 | "name" : "h2", 26 | "ip": "192.168.99.11" 27 | } 28 | ] 29 | }, 30 | { 31 | "cidr": "10.128.0.0/9", 32 | "groups": [{ 33 | "name" : "h3", 34 | "ip": "192.168.99.12" 35 | }, 36 | { 37 | "name" : "h4", 38 | "ip": "192.168.99.13" 39 | } 40 | ] 41 | } 42 | ] 43 | }] 44 | }, 45 | { 46 | "networks": ["net2"], 47 | "map": [{ 48 | "cidr": "11.0.0.0/8", 49 | "groups": [{ 50 | "name" : "h5", 51 | "ip": "192.168.99.10" 52 | }, 53 | { 54 | "name" : "h6", 55 | "ip": "192.168.99.11" 56 | } 57 | ] 58 | }] 59 | 60 | } 61 | ] 62 | } -------------------------------------------------------------------------------- /common/client/testdata/TestPrefixGenForEmptyGroups.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks": [ 3 | { 4 | "name": "net1", 5 | "cidr": "10.0.0.0/16", 6 | "block_mask": 30 7 | } 8 | ], 9 | "topologies": [ 10 | { 11 | "networks": [ 12 | "net1" 13 | ], 14 | "map": [ 15 | { 16 | "groups": [], 17 | "name": "group1" 18 | }, 19 | { 20 | "groups": [ 21 | { 22 | "groups": [], 23 | "name": "group2a" 24 | }, 25 | { 26 | "groups": [], 27 | "name": "group2b" 28 | } 29 | ], 30 | "name": "group2" 31 | } 32 | ] 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /common/client/testdata/TestRepeatedNetwork.json: -------------------------------------------------------------------------------- 1 | { "networks" : [ 2 | { "name" : "net1", "cidr" : "100.11.0.0/20", "block_mask" : 30 }, 3 | { "name" : "net2", "cidr" : "100.22.0.0/20", "block_mask" : 30 } 4 | ], 5 | "topologies" : [ 6 | { 7 | "networks" : ["net1","net2"], 8 | "map" : [ { "groups" : [] } ] 9 | }, 10 | { 11 | "networks" : ["net1","net2"], 12 | "map" : [ { "groups" : [] } ] 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /common/client/testdata/TestSegments.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"net1", 5 | "cidr":"10.0.0.0/24", 6 | "block_mask":30 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "net1" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"foo", 17 | "groups": [{ 18 | "name":"host1", 19 | "ip":"192.168.0.1" 20 | }] 21 | } 22 | ] 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /common/client/testdata/TestTenants.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"net1", 5 | "cidr":"10.200.0.0/16", 6 | "block_mask":29, 7 | "tenants":[ 8 | "tenant1", 9 | "tenant2" 10 | ] 11 | }, 12 | { 13 | "name":"net2", 14 | "cidr":"10.220.0.0/16", 15 | "block_mask":28, 16 | "tenants":[ 17 | "tenant3" 18 | ] 19 | }, 20 | { 21 | "name":"net3", 22 | "cidr":"10.240.0.0/16", 23 | "block_mask":28 24 | } 25 | ], 26 | "topologies":[ 27 | { 28 | "networks":[ 29 | "net1", 30 | "net2", 31 | "net3" 32 | ], 33 | "map":[ 34 | { 35 | "routing":"foo", 36 | "groups": [{ 37 | "name":"host1", 38 | "ip":"192.168.0.1" 39 | }] 40 | } 41 | ] 42 | } 43 | ] 44 | } -------------------------------------------------------------------------------- /common/client/testdata/TestTenantsBug701.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { "name":"net1", "cidr":"10.200.0.0/26", "block_mask":29 }, 4 | { "name":"net2", "cidr":"10.220.0.0/26", "block_mask":29 }, 5 | { "name":"net3", "cidr":"10.240.0.0/26", "block_mask":29 } 6 | ], 7 | "topologies":[ 8 | { 9 | "networks":[ 10 | "net1", 11 | "net2" 12 | ], 13 | "map":[ 14 | { 15 | "groups": [ 16 | { "name":"host1", "ip":"192.168.0.1" }, 17 | { "name":"host2", "ip":"192.168.0.2" }, 18 | { "name":"host3", "ip":"192.168.0.3" }, 19 | { "name":"host4", "ip":"192.168.0.4" } 20 | ] 21 | } 22 | ] 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /common/client/testdata/TestTopologyGetOutputKubeadm.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks": [ 3 | { 4 | "name": "romana-network", 5 | "cidr": "10.112.0.0/12", 6 | "block_mask": 29, 7 | "tenants": [ 8 | "*" 9 | ] 10 | } 11 | ], 12 | "topologies": [ 13 | { 14 | "networks": [ 15 | "romana-network" 16 | ], 17 | "map": [ 18 | { 19 | "groups": [ 20 | { 21 | "assignment": { 22 | "beta.kubernetes.io/arch": "amd64", 23 | "beta.kubernetes.io/os": "linux", 24 | "kubernetes.io/hostname": "ip-192-168-99-10", 25 | "node-role.kubernetes.io/master": "" 26 | }, 27 | "name": "ip-192-168-99-10", 28 | "ip": "192.168.99.10" 29 | } 30 | ], 31 | "name": "host-group-01", 32 | "cidr": "10.112.0.0/15" 33 | }, 34 | { 35 | "name": "host-group-02", 36 | "cidr": "10.114.0.0/15" 37 | }, 38 | { 39 | "name": "host-group-03", 40 | "cidr": "10.116.0.0/15" 41 | }, 42 | { 43 | "name": "host-group-04", 44 | "cidr": "10.118.0.0/15" 45 | }, 46 | { 47 | "name": "host-group-05", 48 | "cidr": "10.120.0.0/15" 49 | }, 50 | { 51 | "name": "host-group-06", 52 | "cidr": "10.122.0.0/15" 53 | }, 54 | { 55 | "name": "host-group-07", 56 | "cidr": "10.124.0.0/15" 57 | }, 58 | { 59 | "name": "host-group-08", 60 | "cidr": "10.126.0.0/15" 61 | } 62 | ] 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /common/client/testdata/TestUpdateTopology.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks":[ 3 | { 4 | "name":"net1", 5 | "cidr":"10.0.0.0/8", 6 | "block_mask":30 7 | } 8 | ], 9 | "topologies":[ 10 | { 11 | "networks":[ 12 | "net1" 13 | ], 14 | "map":[ 15 | { 16 | "routing":"test", 17 | "groups":[ 18 | { 19 | "name":"h1", 20 | "ip":"192.168.99.10" 21 | }, 22 | { 23 | "name":"h2", 24 | "ip":"192.168.99.11" 25 | } 26 | ] 27 | } 28 | ] 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /common/client/testdata/TestUpdateTopologyInvalidBlockMask.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks": [ 3 | { 4 | "name": "net1", 5 | "cidr": "10.0.0.0/24", 6 | "block_mask": 20 7 | } 8 | ], 9 | "topologies": [ 10 | { 11 | "networks": [ 12 | "net1" 13 | ], 14 | "map": [ 15 | { 16 | "groups": [] 17 | } 18 | ] 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /common/config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package common 17 | 18 | // Config is the configuration required for a Romana client library. 19 | // TODO it is here temporarily until circular imports are resolved. 20 | type Config struct { 21 | EtcdEndpoints []string 22 | EtcdPrefix string 23 | InitialTopologyFile *string 24 | Mock bool 25 | } 26 | -------------------------------------------------------------------------------- /common/defs.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package common 17 | 18 | // Definitions of common structures. 19 | 20 | type FindFlag string 21 | 22 | const ( 23 | // Flags to store.Find operation 24 | FindFirst = "findFirst" 25 | FindLast = "findLast" 26 | FindExactlyOne = "findExactlyOne" 27 | FindAll = "findAll" 28 | ) 29 | 30 | // Here we only keep type definitions and struct definitions with no behavior. 31 | 32 | // Constants 33 | const ( 34 | // For passing in Gorilla Mux context the unmarshalled data 35 | ContextKeyUnmarshalledMap string = "UnmarshalledMap" 36 | // For passing in Gorilla Mux context path variables 37 | ContextKeyQueryVariables string = "QueryVars" 38 | // For passing in Gorilla Mux context the original body data 39 | ContextKeyOriginalBody string = "OriginalBody" 40 | ContextKeyMarshaller string = "Marshaller" 41 | ContextKeyUser string = "User" 42 | ReadWriteTimeoutDelta = 10 43 | 44 | // Name of the query parameter used for request token 45 | RequestTokenQueryParameter = "RequestToken" 46 | 47 | HeaderContentType = "content-type" 48 | 49 | Starting ServiceMessage = "Starting." 50 | 51 | // JSON 52 | TimeoutMessage = "{ \"error\" : \"Timed out\" }" 53 | 54 | // Empty string returned when there is a string return 55 | // but there is an error so no point in returning any 56 | // value. 57 | ErrorNoValue = "" 58 | 59 | // Path for authentication; if this is what is used 60 | // in the request we will not check the token (because 61 | // we are attempting to get a token at this point). 62 | AuthPath = "/auth" 63 | 64 | // Body provided. 65 | HookExecutableBodyArgument = "body" 66 | ) 67 | 68 | // LinkResponse structure represents the commonly occurring 69 | // { 70 | // "href" : "https://", 71 | // "rel" : "self" 72 | // } 73 | // part of the response. 74 | type LinkResponse struct { 75 | Href string `json:"href,omitempty"` 76 | Rel string `json:"rel,omitempty"` 77 | } 78 | 79 | // Type definitions 80 | type ServiceMessage string 81 | 82 | // Message to register with the root service the actual 83 | // port a service is listening on. 84 | type PortUpdateMessage struct { 85 | Port uint64 `json:"port"` 86 | } 87 | 88 | // RestServiceInfo describes information about a running 89 | // Romana service. 90 | type RestServiceInfo struct { 91 | // Address being listened on (as host:port) 92 | Address string 93 | // Channel to communicate with the service 94 | Channel chan ServiceMessage 95 | } 96 | -------------------------------------------------------------------------------- /common/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | /* 17 | Package common contains various things common to all Romana services. 18 | */ 19 | package common 20 | -------------------------------------------------------------------------------- /common/log/trace/trace.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package trace 17 | 18 | // These definitions are just helpful conventions for our use of trace 19 | // messages. There is no 'standard' for logging trace levels, but for our 20 | // projects we use them as described below. 21 | 22 | const ( 23 | Public int = 1 // Messages on entry of public/exported functions 24 | Private int = 2 // Messages on entry of private/non-exported functions 25 | Inside int = 3 // Messages from inside of functions 26 | ) 27 | -------------------------------------------------------------------------------- /common/rest_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // +build ignore 17 | 18 | package common 19 | 20 | import ( 21 | "fmt" 22 | "testing" 23 | "time" 24 | 25 | "github.com/codegangsta/negroni" 26 | "github.com/gorilla/mux" 27 | ) 28 | 29 | func TestPanicHandler(t *testing.T) { 30 | var err error 31 | // Create negroni 32 | negroni := negroni.New() 33 | negroni.Use(newPanicRecoveryHandler()) 34 | 35 | router := mux.NewRouter().StrictSlash(true) 36 | handlerOk := func(input interface{}, context RestContext) (interface{}, error) { 37 | return "OK", nil 38 | } 39 | route := Route{Handler: handlerOk, 40 | Method: "GET", 41 | Pattern: "/ok", 42 | AuthZChecker: func(ctx RestContext) bool { 43 | return true 44 | }, 45 | } 46 | wrappedHandler := wrapHandler(handlerOk, route) 47 | router. 48 | Methods(route.Method). 49 | Path(route.Pattern). 50 | Handler(wrappedHandler) 51 | 52 | handlerPanic := func(input interface{}, context RestContext) (interface{}, error) { 53 | panic("Panic!!!") 54 | } 55 | route = Route{Handler: handlerOk, 56 | Method: "GET", 57 | Pattern: "/panic", 58 | AuthZChecker: func(ctx RestContext) bool { 59 | return true 60 | }, 61 | } 62 | wrappedHandler = wrapHandler(handlerPanic, route) 63 | router. 64 | Methods(route.Method). 65 | Path(route.Pattern). 66 | Handler(wrappedHandler) 67 | 68 | negroni.UseHandler(router) 69 | 70 | readWriteDur, _ := time.ParseDuration("10s") 71 | svcInfo, err := RunNegroni(negroni, "localhost:0", readWriteDur) 72 | if err != nil { 73 | t.Errorf("Unexpected error %s", err) 74 | } 75 | t.Log(<-svcInfo.Channel) 76 | 77 | t.Logf("Listening on %s", svcInfo.Address) 78 | 79 | rc, err := NewRestClient(RestClientConfig{ 80 | TimeoutMillis: 10000, 81 | }) 82 | if err != nil { 83 | t.Errorf("Unexpected error %s", err) 84 | } 85 | 86 | url := fmt.Sprintf("http://%s", svcInfo.Address) 87 | s := "" 88 | 89 | okUrl := fmt.Sprintf("%s/ok", url) 90 | err = rc.Get(okUrl, &s) 91 | if err != nil { 92 | t.Errorf("Unexpected error on %s: %s", okUrl, err) 93 | } 94 | t.Logf("Got %s from %s", s, okUrl) 95 | 96 | panicUrl := fmt.Sprintf("%s/panic", url) 97 | err = rc.Get(panicUrl, &s) 98 | if err == nil { 99 | t.Errorf("Expected error on %s, got %+v", panicUrl, s) 100 | } 101 | switch err := err.(type) { 102 | case HttpError: 103 | if err.StatusCode != 500 { 104 | t.Errorf("Unexpected error on %s: %s", okUrl, err) 105 | } 106 | t.Logf("Got expected error on panic: %+v", s) 107 | default: 108 | t.Errorf("Unexpected error on %s: %s", okUrl, err) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /common/structures.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package common 17 | 18 | import ( 19 | "net" 20 | ) 21 | 22 | type IP struct { 23 | ip net.IP 24 | ipv6 bool 25 | } 26 | 27 | func MakeIPv4(a, b, c, d byte) IP { 28 | ip := IP{net.IPv4(a, b, c, d), false} 29 | return ip 30 | } 31 | 32 | func IPv4ToInt(ip net.IP) uint64 { 33 | switch len(ip) { 34 | case 4: // IPv4 35 | return uint64(ip[0])<<24 | uint64(ip[1])<<16 | uint64(ip[2])<<8 | uint64(ip[3]) 36 | case 16: // IPv6 37 | return uint64(ip[12])<<24 | uint64(ip[13])<<16 | uint64(ip[14])<<8 | uint64(ip[15]) 38 | default: 39 | return 0 40 | } 41 | } 42 | 43 | func IntToIPv4(ipInt uint64) net.IP { 44 | return net.IPv4(byte(ipInt>>24), byte(ipInt>>16), byte(ipInt>>8), byte(ipInt)) 45 | } 46 | -------------------------------------------------------------------------------- /common/testdata/agent_proxy_add_policy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | body=`echo $1 | cut -d'=' -f2` 3 | echo Posting $body to http://localhost:9630/ 4 | echo curl -H 'content-type: application/json' -d @- -X POST http://localhost:9630/ 5 | echo $body | curl -H 'content-type: application/json' -d @- -X POST http://localhost:9630/ 6 | -------------------------------------------------------------------------------- /common/testdata/agent_proxy_delete_policy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | body=`echo $1 | cut -d'=' -f2` 3 | echo $body | curl -H 'content-type: application/json' -d @- -X DELETE http://localhost:9630/ 4 | -------------------------------------------------------------------------------- /common/testdata/demo.rsa: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAn8MGiedFoscQZ3Kx0AcM/O28DFjOsdEM8111Dw1pxMQn+fN7 3 | Bx1zmt4eHyYfGhfk4iQop4p2jNgMZLZGfIgTRADdDR8ibtd0x35HloUV6sCsT2RM 4 | Ww2+otAOMmY3Nt+sTphHchbEVAF3kFg6Wdbw7yHP1IRWRzzES0ZY8RPqi1S1JZ1X 5 | WthrDfOhPIxKhAyN20TrwUrIZ2sFzhAY/SBUvGjSF2rVYfK+LfS+GfWb5YpEjTAI 6 | tOd5MWYt4RmVtruKHc/SlaIUjfR3mDHtFsuZmKBp2pPGHSHlubEK8GXE54fYlo9F 7 | opPG1zGQpwk6vulm1mP4PZCR/jj4BfX6HNg5jQIDAQABAoIBAGAwKaWpyvFgbO37 8 | zjbzp8w4znmYlonU1blI0YvIhZ/kgjUpdS8peksJ5JTYfO1W6YWGQ7vrz8io431X 9 | iUS9IOxWoMCfuDmIG1VpIGztn+6BJr1+I8LGmbqpDYMmq6OsM6/2Wz3Dm10ArR4J 10 | IMiqctFnSBwpuVCWs5CmwDIVzaco24mzdZVReQt/mIr6PDy2EWL62NJAU22+g+ki 11 | vFhQ95meiegkLgIiwFcj322Kzkb4Bovhfk/bJMW3yYJ4tNzIjvqa2yaBk45JRtKK 12 | w/jss6wppU52LhHXUqO3jIY0IPAcbbxwkI35u3tCUl8keputm9uOeltKfqUc0RHN 13 | hjVXf7kCgYEAylJvUXRHlwof25FhgcXtWv0/Idis7EPuPjVssTiOxbx6ppJ9F3gz 14 | iVHkJ8JUGlJvLAvvmxC8VG0ooCf7DLO2F/paA/W2Veey5zuD88wsveV0+RBYoyOQ 15 | 8ah7rpKqM6WsmUGigo3kv9/bmFR3QzVqGt0zxmNz+06scOQS0DZ4BjMCgYEAyiXu 16 | 39Zna4Osgc0lUD43mwfG/K0JhbtvRioob9T8ypS10a7np7w/rVCcvEZBNrf0iOOb 17 | r51VdCWldL40U3Ac3ma3BnNoujm/M5yt0+WtX9BonK+82jt2HcPHzetOwfrbWij8 18 | nK6MlHYIdHCT5uas258EYD331BpUmdOWX2gVgT8CgYAlWfCpJgFtdfYJsB5DOgom 19 | ySgVlI3gFG5wV6aYVSuuf6xZAiRL5+oDyPWJDYw/S8awaAe1JfLO11QvtRHY93h/ 20 | lr11i7A8zciufcNPh/sel7Tvoj/q5k8E/ijPQs5fOI/Tv1VEKvP3JFnDFpzp8HuF 21 | OB8t2gB4fb7CHKkIfXXNFwKBgQCgQGYYCK4/60isNecor8oBGq9CzCJKZAaYhv87 22 | eItWRYHF0nRO4stQKCG4l7FHIZm3k0hudbmys81wuQBeTPh1ztO1R3R0fSb4UXL0 23 | EpXY6SylWiPKlWzTt8sDkVEEMkwGM4ve4fkMEG4vcLevSpsqF+uuKDH5cU40DCqf 24 | Q7IYnQKBgCfX8WSqvbdMkK9MQF+p1zFQohajTTN5k2ieZkVoKZFVunArQs3QNSR+ 25 | EkXk1BdHehHqSOo4Lu0nbWKBEm9bitxHZBiIlkGjo785jGMaokMbY8P5PM9SaCxd 26 | iwiAOSRpyqw7gGQGCPJ24tLiGwHw0CAZE9kMLaALsTA1Ggrr2gpG 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /common/testdata/demo.rsa.pkcs8: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCfwwaJ50WixxBn 3 | crHQBwz87bwMWM6x0QzzXXUPDWnExCf583sHHXOa3h4fJh8aF+TiJCininaM2Axk 4 | tkZ8iBNEAN0NHyJu13THfkeWhRXqwKxPZExbDb6i0A4yZjc236xOmEdyFsRUAXeQ 5 | WDpZ1vDvIc/UhFZHPMRLRljxE+qLVLUlnVda2GsN86E8jEqEDI3bROvBSshnawXO 6 | EBj9IFS8aNIXatVh8r4t9L4Z9ZvlikSNMAi053kxZi3hGZW2u4odz9KVohSN9HeY 7 | Me0Wy5mYoGnak8YdIeW5sQrwZcTnh9iWj0Wik8bXMZCnCTq+6WbWY/g9kJH+OPgF 8 | 9foc2DmNAgMBAAECggEAYDAppanK8WBs7fvONvOnzDjOeZiWidTVuUjRi8iFn+SC 9 | NSl1Lyl6SwnklNh87VbphYZDu+vPyKjjfVeJRL0g7FagwJ+4OYgbVWkgbO2f7oEm 10 | vX4jwsaZuqkNgyaro6wzr/ZbPcObXQCtHgkgyKpy0WdIHCm5UJazkKbAMhXNpyjb 11 | ibN1lVF5C3+Yivo8PLYRYvrY0kBTbb6D6SK8WFD3mZ6J6CQuAiLAVyPfbYrORvgG 12 | i+F+T9skxbfJgni03MiO+prbJoGTjklG0orD+OyzrCmlTnYuEddSo7eMhjQg8Bxt 13 | vHCQjfm7e0JSXyR6m62b2456W0p+pRzREc2GNVd/uQKBgQDKUm9RdEeXCh/bkWGB 14 | xe1a/T8h2KzsQ+4+NWyxOI7FvHqmkn0XeDOJUeQnwlQaUm8sC++bELxUbSigJ/sM 15 | s7YX+loD9bZV57LnO4PzzCy95XT5EFijI5DxqHuukqozpayZQaKCjeS/39uYVHdD 16 | NWoa3TPGY3P7Tqxw5BLQNngGMwKBgQDKJe7f1mdrg6yBzSVQPjebB8b8rQmFu29G 17 | Kihv1PzKlLXRruenvD+tUJy8RkE2t/SI45uvnVV0JaV0vjRTcBzeZrcGc2i6Ob8z 18 | nK3T5a1f0Gicr7zaO3Ydw8fN607B+ttaKPycroyUdgh0cJPm5qzbnwRgPffUGlSZ 19 | 05ZfaBWBPwKBgCVZ8KkmAW119gmwHkM6CibJKBWUjeAUbnBXpphVK65/rFkCJEvn 20 | 6gPI9YkNjD9LxrBoB7Ul8s7XVC+1Edj3eH+WvXWLsDzNyK59w0+H+x6XtO+iP+rm 21 | TwT+KM9Czl84j9O/VUQq8/ckWcMWnOnwe4U4Hy3aAHh9vsIcqQh9dc0XAoGBAKBA 22 | ZhgIrj/rSKw15yivygEar0LMIkpkBpiG/zt4i1ZFgcXSdE7iy1AoIbiXsUchmbeT 23 | SG51ubKzzXC5AF5M+HXO07VHdHR9JvhRcvQSldjpLKVaI8qVbNO3ywORUQQyTAYz 24 | i97h+QwQbi9wt69KmyoX664oMflxTjQMKp9DshidAoGAJ9fxZKq9t0yQr0xAX6nX 25 | MVCiFqNNM3mTaJ5mRWgpkVW6cCtCzdA1JH4SReTUF0d6EepI6jgu7SdtYoESb1uK 26 | 3EdkGIiWQaOjvzmMYxqiQxtjw/k8z1JoLF2LCIA5JGnKrDuAZAYI8nbi0uIbAfDQ 27 | IBkT2QwtoAuxMDUaCuvaCkY= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /common/testdata/demo.rsa.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCfwwaJ50WixxBncrHQBwz87bwMWM6x0QzzXXUPDWnExCf583sHHXOa3h4fJh8aF+TiJCininaM2AxktkZ8iBNEAN0NHyJu13THfkeWhRXqwKxPZExbDb6i0A4yZjc236xOmEdyFsRUAXeQWDpZ1vDvIc/UhFZHPMRLRljxE+qLVLUlnVda2GsN86E8jEqEDI3bROvBSshnawXOEBj9IFS8aNIXatVh8r4t9L4Z9ZvlikSNMAi053kxZi3hGZW2u4odz9KVohSN9HeYMe0Wy5mYoGnak8YdIeW5sQrwZcTnh9iWj0Wik8bXMZCnCTq+6WbWY/g9kJH+OPgF9foc2DmN grisha@MacBook-Pro-4.local 2 | -------------------------------------------------------------------------------- /common/testdata/demo.rsa.pub.pkcs8: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn8MGiedFoscQZ3Kx0AcM 3 | /O28DFjOsdEM8111Dw1pxMQn+fN7Bx1zmt4eHyYfGhfk4iQop4p2jNgMZLZGfIgT 4 | RADdDR8ibtd0x35HloUV6sCsT2RMWw2+otAOMmY3Nt+sTphHchbEVAF3kFg6Wdbw 5 | 7yHP1IRWRzzES0ZY8RPqi1S1JZ1XWthrDfOhPIxKhAyN20TrwUrIZ2sFzhAY/SBU 6 | vGjSF2rVYfK+LfS+GfWb5YpEjTAItOd5MWYt4RmVtruKHc/SlaIUjfR3mDHtFsuZ 7 | mKBp2pPGHSHlubEK8GXE54fYlo9FopPG1zGQpwk6vulm1mP4PZCR/jj4BfX6HNg5 8 | jQIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /common/testdata/romana.auth.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | - service: root 3 | api: 4 | host: localhost 5 | port: 9600 6 | auth_public: ../common/testdata/demo.rsa.pub.pkcs8 7 | config: 8 | auth: yes 9 | auth_private: ../common/testdata/demo.rsa.pkcs8 10 | store: 11 | type: sqlite3 12 | database: /tmp/auth.sqlite3 13 | - service: ipam 14 | api: 15 | host: localhost 16 | port: 9601 17 | auth_public: ../common/testdata/demo.rsa.pub.pkcs8 18 | config: 19 | store: 20 | type: sqlite3 21 | database: /tmp/ipam.sqlite3 22 | - service: tenant 23 | api: 24 | host: localhost 25 | port: 9602 26 | auth_public: ../common/testdata/demo.rsa.pub.pkcs8 27 | config: 28 | store: 29 | type: sqlite3 30 | host: localhost 31 | database: /tmp/tenant.sqlite3 32 | - service: topology 33 | api: 34 | host: localhost 35 | port: 9603 36 | auth_public: ../common/testdata/demo.rsa.pub.pkcs8 37 | config: 38 | store: 39 | type: sqlite3 40 | database: /tmp/topology.sqlite3 41 | datacenter: 42 | ip_version: 4 43 | cidr: 10.0.0.0/8 44 | host_bits: 8 45 | tenant_bits: 4 46 | segment_bits: 4 47 | endpoint_space_bits: 0 48 | endpoint_bits: 8 49 | - service: agent 50 | api: 51 | host: 0.0.0.0 52 | port: 9604 53 | auth_public: ../common/testdata/demo.rsa.pub.pkcs8 54 | config: 55 | lease_file : "/etc/ethers" 56 | wait_for_iface_try : 6 57 | store: 58 | type: sqlite3 59 | database: /var/tmp/agent.sqlite3 60 | - service: policy 61 | api: 62 | host: localhost 63 | port: 9605 64 | auth_public: ../common/testdata/demo.rsa.pub.pkcs8 65 | config: 66 | store: 67 | type: sqlite3 68 | database: /tmp/topology.sqlite3 69 | -------------------------------------------------------------------------------- /common/testdata/romana.hooks.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | - service: root 3 | api: 4 | host: localhost 5 | hooks: 6 | - executable: ../common/testdata/hook.sh 7 | pattern: /config/{serviceName} 8 | method: GET 9 | when: before 10 | output: /tmp/hook.txt 11 | - executable: ../common/testdata/hook_bad.sh 12 | pattern: /config/{serviceName}/port 13 | method: POST 14 | when: after 15 | output: /tmp/hook_bad.txt 16 | config: 17 | store: 18 | type: sqlite3 19 | database: /var/tmp/auth.sqlite3 20 | - service: ipam 21 | api: 22 | host: localhost 23 | config: 24 | store: 25 | type: sqlite3 26 | database: /var/tmp/ipam.sqlite3 27 | - service: tenant 28 | api: 29 | host: localhost 30 | config: 31 | store: 32 | type: sqlite3 33 | database: /var/tmp/tenant.sqlite3 34 | - service: topology 35 | api: 36 | host: localhost 37 | config: 38 | store: 39 | type: sqlite3 40 | database: /var/tmp/topology.sqlite3 41 | datacenter: 42 | ip_version: 4 43 | cidr: 10.0.0.0/8 44 | host_bits: 8 45 | tenant_bits: 4 46 | segment_bits: 4 47 | endpoint_space_bits: 0 48 | endpoint_bits: 8 49 | - service: agent 50 | api: 51 | host: 0.0.0.0 52 | config: 53 | lease_file : "/etc/ethers" 54 | wait_for_iface_try : 6 -------------------------------------------------------------------------------- /common/testdata/romana.sample.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | - service: root 3 | api: 4 | host: localhost 5 | port: 9600 6 | config: 7 | store: 8 | type: sqlite3 9 | database: /var/tmp/ipam.sqlite3 10 | - service: ipam 11 | api: 12 | host: localhost 13 | port: 9601 14 | config: 15 | store: 16 | endpoints: 17 | - localhost:2379 18 | prefix: /romana/ipam 19 | - service: policy 20 | api: 21 | host: localhost 22 | port: 9602 23 | config: 24 | store: 25 | endpoints: 26 | - localhost:2379 27 | prefix: /romana/policy 28 | - service: agent 29 | api: 30 | host: 0.0.0.0 31 | port: 9603 32 | hooks: 33 | - executable: ../common/testdata/agent_proxy_add_policy.sh 34 | pattern: /policies 35 | method: POST 36 | when: before 37 | output: /var/log/policies-hook.log 38 | - executable: ../common/testdata/agent_proxy_delete_policy.sh 39 | pattern: /policies 40 | method: DELETE 41 | when: before 42 | output: /var/log/policies-hook.log 43 | config: 44 | lease_file : "/etc/ethers" 45 | wait_for_iface_try : 6 46 | store: 47 | type: sqlite3 48 | database: /var/tmp/agent.sqlite3 49 | - service: kubernetesListener 50 | api: 51 | host: 0.0.0.0 52 | port: 9604 53 | config: 54 | kubernetes_url : "http://localhost" 55 | namespace_notification_path: "/api/v1/namespaces/?watch=true" 56 | policy_notification_path_prefix : "/apis/extensions/v1beta1/namespaces/" 57 | policy_notification_path_postfix : "/networkpolicies/?watch=true" 58 | segment_label_name: "tier" 59 | -------------------------------------------------------------------------------- /common/testdata/romanad.yaml: -------------------------------------------------------------------------------- 1 | romanad: 2 | host: localhost 3 | port: 9600 4 | etcd_prefix: /romana 5 | etcd_endpoints: 6 | - localhost:2379 7 | agent: 8 | host: 0.0.0.0 9 | port: 9604 10 | lease_file : "/etc/ethers" 11 | wait_for_iface_try : 6 12 | kubernetesListener 13 | host: 0.0.0.0 14 | port: 9606 15 | kubernetes_url : "http://localhost" 16 | namespace_notification_path: "/api/v1/namespaces/?watch=true" 17 | policy_notification_path_prefix : "/apis/extensions/v1beta1/namespaces/" 18 | policy_notification_path_postfix : "/networkpolicies/?watch=true" 19 | segment_label_name: "tier" 20 | 21 | -------------------------------------------------------------------------------- /doc/policy-examples/example-policy-dhcp-host.json: -------------------------------------------------------------------------------- 1 | { 2 | "ingress": [ 3 | { 4 | "rules": [ 5 | { 6 | "ports": [ 7 | 67 8 | ], 9 | "protocol": "UDP" 10 | } 11 | ], 12 | "peers": [ 13 | { 14 | "peer": "local" 15 | } 16 | ] 17 | } 18 | ], 19 | "applied_to": [ 20 | { 21 | "dest": "host" 22 | } 23 | ], 24 | "name": "pol-dhcp-host", 25 | "direction": "ingress" 26 | } 27 | -------------------------------------------------------------------------------- /doc/policy-examples/example-policy-dhcp-vm.json: -------------------------------------------------------------------------------- 1 | { 2 | "applied_to": [ 3 | { 4 | "dest": "local" 5 | } 6 | ], 7 | "ingress": [ 8 | { 9 | "peers": [ 10 | { 11 | "peer": "host" 12 | } 13 | ], 14 | "rules": [ 15 | { 16 | "ports": [ 17 | 68 18 | ], 19 | "protocol": "UDP" 20 | } 21 | ] 22 | } 23 | ], 24 | "name": "pol-dhcp-vm", 25 | "direction": "ingress" 26 | } 27 | -------------------------------------------------------------------------------- /doc/policy-examples/example-policy-icmp-vm.json: -------------------------------------------------------------------------------- 1 | { 2 | "ingress": [ 3 | { 4 | "rules": [ 5 | { 6 | "protocol": "ICMP" 7 | } 8 | ], 9 | "peers": [ 10 | { 11 | "peer": "host" 12 | } 13 | ] 14 | } 15 | ], 16 | "applied_to": [ 17 | { 18 | "dest": "local" 19 | } 20 | ], 21 | "name": "pol-icmp-vm", 22 | "direction": "ingress" 23 | } 24 | -------------------------------------------------------------------------------- /doc/policy-examples/example-policy-ssh-vm.json: -------------------------------------------------------------------------------- 1 | { 2 | "ingress": [ 3 | { 4 | "rules": [ 5 | { 6 | "ports": [ 7 | 22 8 | ], 9 | "protocol": "TCP" 10 | } 11 | ], 12 | "applied_to": [ 13 | { 14 | "dest": "local" 15 | } 16 | ] 17 | } 18 | ], 19 | "peers": [ 20 | { 21 | "peer": "host" 22 | } 23 | ], 24 | "name": "pol-ssh-vm", 25 | "direction": "ingress" 26 | } 27 | -------------------------------------------------------------------------------- /doc/policy-examples/policy-service-agent.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pa-pol", 3 | "description": "Policy Service <--> Agent policy example.", 4 | "rules": [ 5 | { 6 | "ports": [ 7 | 80, 8 | 443 9 | ], 10 | "protocol": "TCP" 11 | } 12 | ], 13 | "peers": [ 14 | { 15 | "tenant_network_id": 1, 16 | "segment_network_id": 2 17 | } 18 | ], 19 | "direction": "ingress", 20 | "external_id": "external_id_1", 21 | "applied_to": [ 22 | { 23 | "tenant_network_id": 1, 24 | "segment_network_id": 1 25 | } 26 | ], 27 | "datacenter": { 28 | "ip_version": 4, 29 | "cidr": "10.0.0.0/8", 30 | "port_bits": 8, 31 | "tenant_bits": 4, 32 | "segment_bits": 4, 33 | "endpoint_space_bits": 0, 34 | "endpoint_bits": 8 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /doc/policy.md: -------------------------------------------------------------------------------- 1 | ### Romana Policy Service and Policy Examples 2 | 3 | #### Sample Romana Policy 4 | Sample Romana polices with various combinations of rules are shown [here](examples/). 5 | 6 | #### Applying New Policy 7 | Romana policies can be applied by calling the Policy API 8 | directly or using the Romana CLI. An example of applying 9 | a policy is shown below: 10 | ```bash 11 | $ cat policy.json 12 | { 13 | "securitypolicies": [{ 14 | "name": "policy1", 15 | "description": "sample policy opening ssh, http and https ports", 16 | "direction": "ingress", 17 | "applied_to": [{ 18 | "tenant": "demo", 19 | "segment": "default" 20 | }], 21 | "peers": [{ 22 | "peer": "any" 23 | }], 24 | "rules": [{ 25 | "protocol": "tcp", 26 | "ports": [22, 80, 443] 27 | }] 28 | }] 29 | } 30 | 31 | $ romana policy add policy.json 32 | New Policies Processed: 33 | Id Policy Name Direction Successful Applied? 34 | 1 policy1 ingress true 35 | 36 | $ romana policy list -f json 37 | [{ 38 | "direction": "ingress", 39 | "description": "sample policy opening ssh, http and https ports", 40 | "name": "policy1", 41 | "id": 1, 42 | "external_id": "policy1", 43 | "applied_to": [{ 44 | "tenant": "demo", 45 | "segment": "default" 46 | }], 47 | "peers": [{ 48 | "peer": "any" 49 | }], 50 | "rules": [{ 51 | "protocol": "TCP", 52 | "ports": [ 53 | 22, 54 | 80, 55 | 443 56 | ] 57 | }] 58 | }] 59 | ``` 60 | -------------------------------------------------------------------------------- /listener/processor.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package listener 17 | 18 | import ( 19 | "reflect" 20 | "time" 21 | 22 | "github.com/romana/core/common/log/trace" 23 | log "github.com/romana/rlog" 24 | 25 | "k8s.io/client-go/pkg/api/v1" 26 | "k8s.io/client-go/pkg/apis/extensions/v1beta1" 27 | ) 28 | 29 | const ( 30 | // TODO make a parameter. Stas. 31 | processorTickTime = 4 32 | ) 33 | 34 | // process is a goroutine that consumes resource update events coming from 35 | // Kubernetes and: 36 | // 1. On receiving an added or deleted event: 37 | // i. add it to the queue 38 | // ii. on a timer event, send the events to handleNetworkPolicyEvents and empty the queue 39 | // 2. On receiving a done event, exit the goroutine 40 | func (l *KubeListener) process(in <-chan Event, done chan struct{}) { 41 | log.Debugf("KubeListener: process(): Entered with in %v, done %v", in, done) 42 | 43 | timer := time.NewTicker(processorTickTime * time.Second) 44 | var networkPolicyEvents []Event 45 | 46 | go func() { 47 | for { 48 | select { 49 | case <-timer.C: 50 | if len(networkPolicyEvents) > 0 { 51 | log.Infof("Calling network policy handler for scheduled %d events", len(networkPolicyEvents)) 52 | handleNetworkPolicyEvents(networkPolicyEvents, l) 53 | networkPolicyEvents = nil 54 | } 55 | case e := <-in: 56 | log.Debugf("KubeListener: process(): Got %v", e) 57 | switch obj := e.Object.(type) { 58 | case *v1beta1.NetworkPolicy: 59 | log.Tracef(trace.Inside, "Scheduing network policy action, now scheduled %d actions", len(networkPolicyEvents)) 60 | networkPolicyEvents = append(networkPolicyEvents, e) 61 | case *v1.Namespace: 62 | log.Tracef(trace.Inside, "Processor received namespace") 63 | handleNamespaceEvent(e, l) 64 | default: 65 | log.Errorf("Processor received an event of unkonwn type %s, ignoring object %s", reflect.TypeOf(obj), obj) 66 | } 67 | case <-done: 68 | log.Debugf("KubeListener: process(): Got done") 69 | timer.Stop() 70 | return 71 | } 72 | } 73 | }() 74 | } 75 | -------------------------------------------------------------------------------- /listener/resources_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // +build ignore 17 | 18 | package listener 19 | 20 | import ( 21 | "testing" 22 | 23 | "github.com/romana/core/common" 24 | "github.com/romana/core/common/client" 25 | 26 | "k8s.io/client-go/pkg/api/v1" 27 | "k8s.io/client-go/pkg/apis/extensions/v1beta1" 28 | ) 29 | 30 | func TestSyncNetworkPolicies(t *testing.T) { 31 | 32 | var allRomanaPolicies []common.Policy 33 | var kubePolicies []v1beta1.NetworkPolicy 34 | getAllPoliciesFunc = func(client *client.Client) ([]common.Policy, error) { 35 | return allRomanaPolicies, nil 36 | } 37 | 38 | allRomanaPolicies = []common.Policy{ 39 | common.Policy{ 40 | Name: "donotdelete", 41 | }, 42 | common.Policy{ 43 | Name: "kube.default.deleteme", 44 | }, 45 | common.Policy{ 46 | Name: "kube.default.newPolicy1", 47 | }, 48 | } 49 | 50 | kubePolicies = []v1beta1.NetworkPolicy{ 51 | v1beta1.NetworkPolicy{ 52 | ObjectMeta: v1.ObjectMeta{Name: "newPolicy1"}, 53 | }, 54 | v1beta1.NetworkPolicy{ 55 | ObjectMeta: v1.ObjectMeta{Name: "newPolicy2"}, 56 | }, 57 | } 58 | 59 | l := &KubeListener{} 60 | newKubePolicies, oldRomanaPolicies, _ := l.syncNetworkPolicies(kubePolicies) 61 | 62 | if len(oldRomanaPolicies) != 1 || len(newKubePolicies) != 1 { 63 | t.Errorf("Received %d newKubePolicies (expect 1) and %d oldRomanaPolicies (expect 1)", len(newKubePolicies), len(oldRomanaPolicies)) 64 | } 65 | 66 | if oldRomanaPolicies[0].Name != "kube.default.deleteme" { 67 | t.Errorf("Wrong romana policy scheduled for deletion %s - expected kube.default.deleteme", oldRomanaPolicies[0]) 68 | } 69 | 70 | newKubePolicy, ok := newKubePolicies[0].Object.(v1beta1.NetworkPolicy) 71 | if !ok { 72 | t.Error("Failed to cast v1beta1.NetworkPolicy") 73 | } 74 | 75 | if newKubePolicy.ObjectMeta.Name != "newPolicy2" { 76 | t.Errorf("Wrong kube policy scheduled for creation %s - expected newPolicy2", newKubePolicy.ObjectMeta.Name) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /listener/testdata/any-source.json: -------------------------------------------------------------------------------- 1 | {"id":"kube.tenant-a.pol1.d3122af3-a4cc-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"peer":"any"}],"rules":[{"protocol":"tcp","ports":[80]}]}]} 2 | -------------------------------------------------------------------------------- /listener/testdata/any-source.kube: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "extensions/v1beta1", 3 | "kind": "NetworkPolicy", 4 | "metadata": { 5 | "name": "pol1", 6 | "namespace": "tenant-a" 7 | }, 8 | "spec": { 9 | "ingress": [ 10 | { 11 | "ports": [ 12 | { 13 | "port": 80, 14 | "protocol": "TCP" 15 | } 16 | ] 17 | } 18 | ], 19 | "podSelector": { 20 | "matchLabels": { 21 | "romana.io/segment": "backend" 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /listener/testdata/any-source.yml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: pol1 5 | namespace: tenant-a 6 | spec: 7 | podSelector: 8 | matchLabels: 9 | romana.io/segment: backend 10 | ingress: 11 | - from: 12 | ports: 13 | - protocol: TCP 14 | port: 80 15 | -------------------------------------------------------------------------------- /listener/testdata/demo-policy.json: -------------------------------------------------------------------------------- 1 | {"id":"kube.tenant-a.pol1.a8e9618f-ab1d-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"tenant_id":"tenant-a","segment_id":"frontend"}],"rules":[{"protocol":"tcp","ports":[80]}]}]} 2 | -------------------------------------------------------------------------------- /listener/testdata/demo-policy.kube: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "extensions/v1beta1", 3 | "kind": "NetworkPolicy", 4 | "metadata": { 5 | "name": "pol1", 6 | "namespace": "tenant-a" 7 | }, 8 | "spec": { 9 | "ingress": [ 10 | { 11 | "from": [ 12 | { 13 | "podSelector": { 14 | "matchLabels": { 15 | "romana.io/segment": "frontend" 16 | } 17 | } 18 | } 19 | ], 20 | "ports": [ 21 | { 22 | "port": 80, 23 | "protocol": "TCP" 24 | } 25 | ] 26 | } 27 | ], 28 | "podSelector": { 29 | "matchLabels": { 30 | "romana.io/segment": "backend" 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /listener/testdata/foreing-source-tenant.json: -------------------------------------------------------------------------------- 1 | {"id":"kube.tenant-a.pol1.0ef621c5-a4cd-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"tenant_id":"kube-system"}],"rules":[{"protocol":"tcp","ports":[80]}]}]} 2 | -------------------------------------------------------------------------------- /listener/testdata/foreing-source-tenant.kube: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "extensions/v1beta1", 3 | "kind": "NetworkPolicy", 4 | "metadata": { 5 | "name": "pol1", 6 | "namespace": "tenant-a" 7 | }, 8 | "spec": { 9 | "ingress": [ 10 | { 11 | "from": [ 12 | { 13 | "namespaceSelector": { 14 | "matchLabels": { 15 | "namespace": "kube-system" 16 | } 17 | } 18 | } 19 | ], 20 | "ports": [ 21 | { 22 | "port": 80, 23 | "protocol": "TCP" 24 | } 25 | ] 26 | } 27 | ], 28 | "podSelector": { 29 | "matchLabels": { 30 | "romana.io/segment": "backend" 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /listener/testdata/foreing-source-tenant.yml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: pol1 5 | namespace: tenant-a 6 | spec: 7 | podSelector: 8 | matchLabels: 9 | romana.io/segment: backend 10 | ingress: 11 | - from: 12 | - namespaceSelector: 13 | matchLabels: 14 | namespace: kube-system 15 | ports: 16 | - protocol: TCP 17 | port: 80 18 | -------------------------------------------------------------------------------- /listener/testdata/no-rules.json: -------------------------------------------------------------------------------- 1 | {"id":"kube.tenant-a.pol1.4bf9aba4-a4cd-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"tenant_id":"tenant-a","segment_id":"frontend"}],"rules":[{"protocol":"any"}]}]} 2 | -------------------------------------------------------------------------------- /listener/testdata/no-rules.kube: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "extensions/v1beta1", 3 | "kind": "NetworkPolicy", 4 | "metadata": { 5 | "name": "pol1", 6 | "namespace": "tenant-a" 7 | }, 8 | "spec": { 9 | "ingress": [ 10 | { 11 | "from": [ 12 | { 13 | "podSelector": { 14 | "matchLabels": { 15 | "romana.io/segment": "frontend" 16 | } 17 | } 18 | } 19 | ] 20 | } 21 | ], 22 | "podSelector": { 23 | "matchLabels": { 24 | "romana.io/segment": "backend" 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /listener/testdata/no-rules.yml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: pol1 5 | namespace: tenant-a 6 | spec: 7 | podSelector: 8 | matchLabels: 9 | romana.io/segment: backend 10 | ingress: 11 | - from: 12 | - podSelector: 13 | matchLabels: 14 | romana.io/segment: frontend 15 | ports: 16 | -------------------------------------------------------------------------------- /listener/testdata/no-source-segment.json: -------------------------------------------------------------------------------- 1 | {"id":"kube.tenant-a.pol1.7bcdb586-a4cd-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"tenant_id":"tenant-a"}],"rules":[{"protocol":"tcp","ports":[80]}]}]} 2 | -------------------------------------------------------------------------------- /listener/testdata/no-source-segment.kube: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "extensions/v1beta1", 3 | "kind": "NetworkPolicy", 4 | "metadata": { 5 | "name": "pol1", 6 | "namespace": "tenant-a" 7 | }, 8 | "spec": { 9 | "ingress": [ 10 | { 11 | "from": [ 12 | { 13 | "podSelector": { 14 | "matchLabels": { 15 | "free-range": "tenbucks" 16 | } 17 | } 18 | } 19 | ], 20 | "ports": [ 21 | { 22 | "port": 80, 23 | "protocol": "TCP" 24 | } 25 | ] 26 | } 27 | ], 28 | "podSelector": { 29 | "matchLabels": { 30 | "romana.io/segment": "backend" 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /listener/testdata/no-source-segment.yml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: pol1 5 | namespace: tenant-a 6 | spec: 7 | podSelector: 8 | matchLabels: 9 | romana.io/segment: backend 10 | ingress: 11 | - from: 12 | - podSelector: 13 | matchLabels: 14 | free-range: tenbucks 15 | ports: 16 | - protocol: TCP 17 | port: 80 18 | -------------------------------------------------------------------------------- /listener/testdata/no-target-segment.json: -------------------------------------------------------------------------------- 1 | {"id":"kube.tenant-a.pol1.a7cac6f1-a4cd-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a"}],"ingress":[{"peers":[{"tenant_id":"tenant-a","segment_id":"frontend"}],"rules":[{"protocol":"tcp","ports":[80]}]}]} 2 | -------------------------------------------------------------------------------- /listener/testdata/no-target-segment.kube: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "extensions/v1beta1", 3 | "kind": "NetworkPolicy", 4 | "metadata": { 5 | "name": "pol1", 6 | "namespace": "tenant-a" 7 | }, 8 | "spec": { 9 | "ingress": [ 10 | { 11 | "from": [ 12 | { 13 | "podSelector": { 14 | "matchLabels": { 15 | "romana.io/segment": "frontend" 16 | } 17 | } 18 | } 19 | ], 20 | "ports": [ 21 | { 22 | "port": 80, 23 | "protocol": "TCP" 24 | } 25 | ] 26 | } 27 | ], 28 | "podSelector": { 29 | "matchLabels": { 30 | "free-range": "tenbucks" 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /listener/testdata/no-target-segment.yml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: pol1 5 | namespace: tenant-a 6 | spec: 7 | podSelector: 8 | matchLabels: 9 | free-range: tenbucks 10 | ingress: 11 | - from: 12 | - podSelector: 13 | matchLabels: 14 | romana.io/segment: frontend 15 | ports: 16 | - protocol: TCP 17 | port: 80 18 | -------------------------------------------------------------------------------- /pkg/policytools/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package policytools 17 | -------------------------------------------------------------------------------- /pkg/policytools/internal/gen/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // +build ignore 17 | 18 | package main 19 | 20 | import ( 21 | "encoding/csv" 22 | "flag" 23 | "os" 24 | "text/template" 25 | ) 26 | 27 | func main() { 28 | templatePath := flag.String("template", "", "template to render") 29 | dataFilePath := flag.String("data", "", "") 30 | outputPath := flag.String("out", "", "") 31 | flag.Parse() 32 | 33 | template, err := template.ParseFiles(*templatePath) 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | data, err := os.Open(*dataFilePath) 39 | if err != nil { 40 | panic(err) 41 | } 42 | 43 | r := csv.NewReader(data) 44 | r.Comma = '\t' 45 | r.LazyQuotes = true 46 | 47 | records, err := r.ReadAll() 48 | if err != nil { 49 | panic(err) 50 | } 51 | 52 | out, err := os.OpenFile(*outputPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) 53 | if err != nil { 54 | panic(err) 55 | } 56 | defer out.Close() 57 | 58 | err = template.Execute(out, records) 59 | if err != nil { 60 | panic(err) 61 | } 62 | 63 | err = out.Close() 64 | if err != nil { 65 | panic(err) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /pkg/policytools/iterator.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package policytools 17 | 18 | import ( 19 | "fmt" 20 | 21 | "github.com/romana/core/common/api" 22 | ) 23 | 24 | // PolicyIterator provides a way to iterate over every combination of a 25 | // target * peer * rule in a list of policies. 26 | type PolicyIterator struct { 27 | policies []api.Policy 28 | policyIdx int 29 | targetIdx int 30 | ingressIdx int 31 | peerIdx int 32 | ruleIdx int 33 | started bool 34 | } 35 | 36 | // New creates a new PolicyIterator. 37 | func NewPolicyIterator(policies []api.Policy) (*PolicyIterator, error) { 38 | if len(policies) == 0 { 39 | return nil, fmt.Errorf("must have non empty policies list") 40 | } 41 | 42 | emptyIngress := func(p api.Policy) bool { 43 | return len(p.Ingress) == 0 44 | } 45 | 46 | emptyRules := func(p api.Policy) bool { 47 | for _, i := range p.Ingress { 48 | if len(i.Rules) == 0 { 49 | return true 50 | } 51 | } 52 | return false 53 | } 54 | 55 | emptyPeers := func(p api.Policy) bool { 56 | for _, i := range p.Ingress { 57 | if len(i.Peers) == 0 { 58 | return true 59 | } 60 | } 61 | return false 62 | } 63 | 64 | emptyTargets := func(p api.Policy) bool { 65 | return len(p.AppliedTo) == 0 66 | } 67 | 68 | for _, p := range policies { 69 | if emptyIngress(p) || emptyTargets(p) || emptyPeers(p) || emptyRules(p) { 70 | return nil, fmt.Errorf("policy %s has .Ingress .AppliedTo .Peers or .Rules field empty", p) 71 | } 72 | } 73 | 74 | return &PolicyIterator{policies: policies}, nil 75 | } 76 | 77 | // Next advances policy iterator to the next combination 78 | // of policy * target * peer * rule. It returns false if iterator 79 | // can not advance any further, otherwise it returs true. 80 | func (i *PolicyIterator) Next() bool { 81 | if !i.started { 82 | i.started = true 83 | return true 84 | } 85 | 86 | policy, _, ingress, _, _ := i.items() 87 | 88 | if i.ruleIdx < len(ingress.Rules)-1 { 89 | i.ruleIdx += 1 90 | return true 91 | } 92 | 93 | if i.peerIdx < len(ingress.Peers)-1 { 94 | i.peerIdx += 1 95 | i.ruleIdx = 0 96 | return true 97 | } 98 | 99 | if i.ingressIdx < len(policy.Ingress)-1 { 100 | i.ingressIdx += 1 101 | i.ruleIdx = 0 102 | i.peerIdx = 0 103 | return true 104 | } 105 | 106 | if i.targetIdx < len(policy.AppliedTo)-1 { 107 | i.targetIdx += 1 108 | i.ingressIdx = 0 109 | i.ruleIdx = 0 110 | i.peerIdx = 0 111 | return true 112 | } 113 | 114 | if i.policyIdx < len(i.policies)-1 { 115 | i.policyIdx += 1 116 | i.targetIdx = 0 117 | i.ingressIdx = 0 118 | i.ruleIdx = 0 119 | i.peerIdx = 0 120 | return true 121 | } 122 | 123 | return false 124 | } 125 | 126 | // Items retrieves current combination of policy * target * peer * rule from iterator. 127 | func (i PolicyIterator) Items() (api.Policy, api.Endpoint, api.Endpoint, api.Rule) { 128 | policy, target, _, peer, rule := i.items() 129 | return policy, target, peer, rule 130 | } 131 | 132 | func (i PolicyIterator) items() (api.Policy, api.Endpoint, api.RomanaIngress, api.Endpoint, api.Rule) { 133 | policy := i.policies[i.policyIdx] 134 | target := policy.AppliedTo[i.targetIdx] 135 | ingress := policy.Ingress[i.ingressIdx] 136 | peer := policy.Ingress[i.ingressIdx].Peers[i.peerIdx] 137 | rule := policy.Ingress[i.ingressIdx].Rules[i.ruleIdx] 138 | return policy, target, ingress, peer, rule 139 | } 140 | -------------------------------------------------------------------------------- /pkg/policytools/templates/blueprint.go_template: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // This file if generate. Do not edit. 17 | 18 | package policytools 19 | 20 | import ( 21 | "github.com/romana/core/agent/firewall" 22 | "github.com/romana/core/common/api" 23 | ) 24 | 25 | var Blueprints = map[string]RuleBlueprint{ 26 | {{ range . }} {{ $Direction := index . 0 }} {{ $Scheme := index . 1}} {{ $PeerType := index . 2}} {{ $TargetType := index . 3}} {{ $BaseChain := index . 4 }} {{ $TopRuleMatch := index . 5 }} {{ $TopRuleAction := index . 6 }} {{ $SecondBaseChain := index . 7 }} {{ $SecondRuleMatch := index . 8 }} {{ $SecondRuleAction := index . 9 }} {{ $ThirdBaseChain := index . 10 }} {{ $ThirdRuleMatch := index . 11 }} {{ $ThirdRuleAction := index . 12 }} {{ $FourthBaseChain := index . 13 }} {{ $FourthRuleMatch := index . 14 }} {{ $FourthRuleAction := index . 15 }} {{ if (ne $BaseChain "BaseChain") }} 27 | MakeBlueprintKey( 28 | {{ $Direction }}, 29 | {{ $Scheme }}, 30 | {{ $TargetType }}, 31 | {{ $PeerType }}, 32 | ): RuleBlueprint{ 33 | BaseChain: {{ $BaseChain }}, 34 | TopRuleMatch: {{ $TopRuleMatch }}, 35 | TopRuleAction: {{ $TopRuleAction }}, 36 | SecondBaseChain: {{ $SecondBaseChain }}, 37 | SecondRuleMatch: {{ $SecondRuleMatch }}, 38 | SecondRuleAction: {{ $SecondRuleAction }}, 39 | ThirdBaseChain: {{ $ThirdBaseChain }}, 40 | ThirdRuleMatch: {{ $ThirdRuleMatch }}, 41 | ThirdRuleAction: {{ $ThirdRuleAction }}, 42 | FourthBaseChain: {{ $FourthBaseChain }}, 43 | FourthRuleMatch: {{ $FourthRuleMatch }}, 44 | FourthRuleAction: "{{ $FourthRuleAction }}", 45 | }, 46 | {{ end }} {{ end }} 47 | } 48 | -------------------------------------------------------------------------------- /routepublisher/publisher/publisher.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // Package defines interface for publishing networks via dynamic routing 17 | // protocols. 18 | package publisher 19 | 20 | import ( 21 | "net" 22 | ) 23 | 24 | type Config map[string]string 25 | 26 | func (c Config) SetDefault(key, defaultValue string) string { 27 | if configValue, ok := c[key]; ok { 28 | return configValue 29 | } 30 | 31 | return defaultValue 32 | } 33 | 34 | type Interface interface { 35 | // Updates list of networks advertised via routing protocol. 36 | Update([]net.IPNet, map[string]interface{}) error 37 | } 38 | -------------------------------------------------------------------------------- /server/romanad.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | package server 17 | 18 | import ( 19 | "github.com/romana/core/common" 20 | "github.com/romana/core/common/api" 21 | "github.com/romana/core/common/client" 22 | ) 23 | 24 | type Romanad struct { 25 | Addr string 26 | client *client.Client 27 | } 28 | 29 | func (r *Romanad) GetAddress() string { 30 | return r.Addr 31 | } 32 | 33 | func (r *Romanad) Name() string { 34 | return "romanad" 35 | } 36 | 37 | func (r *Romanad) Initialize(clientConfig common.Config) error { 38 | var err error 39 | r.client, err = client.NewClient(&clientConfig) 40 | if err != nil { 41 | return err 42 | } 43 | return nil 44 | } 45 | 46 | // Routes provided by ipam. 47 | func (r *Romanad) Routes() common.Routes { 48 | routes := common.Routes{ 49 | common.Route{ 50 | Method: "POST", 51 | Pattern: "/policies", 52 | Handler: r.addPolicy, 53 | MakeMessage: func() interface{} { return &api.Policy{} }, 54 | UseRequestToken: false, 55 | }, 56 | common.Route{ 57 | Method: "DELETE", 58 | Pattern: "/policies", 59 | Handler: r.deletePolicy, 60 | MakeMessage: func() interface{} { return &api.Policy{} }, 61 | UseRequestToken: false, 62 | }, 63 | common.Route{ 64 | Method: "DELETE", 65 | Pattern: "/policies/{policyID}", 66 | Handler: r.deletePolicy, 67 | MakeMessage: nil, 68 | UseRequestToken: false, 69 | }, 70 | common.Route{ 71 | Method: "GET", 72 | Pattern: "/policies", 73 | Handler: r.listPolicies, 74 | MakeMessage: nil, 75 | UseRequestToken: false, 76 | }, 77 | common.Route{ 78 | Method: "GET", 79 | Pattern: "/policies/{policyID}", 80 | Handler: r.getPolicy, 81 | MakeMessage: nil, 82 | UseRequestToken: false, 83 | }, 84 | common.Route{ 85 | Method: "GET", 86 | Pattern: "/networks/{network}/blocks", 87 | Handler: r.listNetworkBlocks, 88 | }, 89 | common.Route{ 90 | Method: "GET", 91 | Pattern: "/blocks", 92 | Handler: r.listAllBlocks, 93 | }, 94 | common.Route{ 95 | Method: "POST", 96 | Pattern: "/address", 97 | Handler: r.allocateIP, 98 | MakeMessage: func() interface{} { return &api.IPAMAddressRequest{} }, 99 | }, 100 | common.Route{ 101 | Method: "DELETE", 102 | Pattern: "/address", 103 | Handler: r.deallocateIP, 104 | }, 105 | common.Route{ 106 | Method: "GET", 107 | Pattern: "/networks", 108 | Handler: r.listNetworks, 109 | }, 110 | common.Route{ 111 | Method: "GET", 112 | Pattern: "/topology", 113 | Handler: r.getTopology, 114 | }, 115 | common.Route{ 116 | Method: "POST", 117 | Pattern: "/topology", 118 | Handler: r.updateTopology, 119 | MakeMessage: func() interface{} { return &api.TopologyUpdateRequest{} }, 120 | }, 121 | common.Route{ 122 | Method: "GET", 123 | Pattern: "/hosts", 124 | Handler: r.listHosts, 125 | }, 126 | common.Route{ 127 | Method: "POST", 128 | Pattern: "/hosts", 129 | Handler: r.addHost, 130 | MakeMessage: func() interface{} { return &api.Host{} }, 131 | }, 132 | } 133 | return routes 134 | } 135 | -------------------------------------------------------------------------------- /test/cmd/fuzzer/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2017 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // Command for running the Kubernetes Listener. 17 | package main 18 | 19 | import ( 20 | "flag" 21 | "fmt" 22 | "net" 23 | 24 | "os" 25 | "strings" 26 | 27 | "github.com/romana/core/common" 28 | "github.com/romana/core/common/api" 29 | "github.com/romana/core/common/client" 30 | log "github.com/romana/rlog" 31 | ) 32 | 33 | func main() { 34 | var err error 35 | endpointsStr := flag.String("etcd-endpoints", client.DefaultEtcdEndpoints, "Comma-separated list of etcd endpoints.") 36 | prefix := flag.String("etcd-prefix", client.DefaultEtcdPrefix, "Prefix to use for etcd data.") 37 | flag.Parse() 38 | if endpointsStr == nil { 39 | log.Errorf("No etcd endpoints specified") 40 | os.Exit(1) 41 | } 42 | endpoints := strings.Split(*endpointsStr, ",") 43 | 44 | pr := *prefix 45 | if !strings.HasPrefix(pr, "/") { 46 | pr = "/" + pr 47 | } 48 | config := common.Config{EtcdEndpoints: endpoints, 49 | EtcdPrefix: pr, 50 | } 51 | cl, err := client.NewClient(&config) 52 | if err != nil { 53 | panic(err) 54 | } 55 | tags := make(map[string]string) 56 | i := 0 57 | for { 58 | i++ 59 | log.Infof("In fuzzer, with %d", i) 60 | ip := net.ParseIP(fmt.Sprintf("10.10.10.%d", i)) 61 | tags[fmt.Sprintf("key%d", i)] = fmt.Sprintf("val%d", i) 62 | hostName := fmt.Sprintf("fuzzerhost%d", i) 63 | host := api.Host{IP: ip, 64 | Name: hostName, 65 | Tags: tags, 66 | } 67 | log.Infof("Adding host %s", hostName) 68 | err = cl.IPAM.AddHost(host) 69 | if err != nil { 70 | panic(err) 71 | } 72 | log.Infof("Added host OK") 73 | addr := fmt.Sprintf("addr%d", i) 74 | log.Infof("Trying to allocate IP for %s", addr) 75 | ip, err := cl.IPAM.AllocateIP(addr, hostName, "t1", "s1") 76 | if err != nil { 77 | panic(err) 78 | } 79 | log.Infof("Allocated %s for %s", ip, addr) 80 | if i > 4 { 81 | for j := 1; j <= 4; j++ { 82 | err = cl.IPAM.DeallocateIP(fmt.Sprintf("addr%d", j)) 83 | if err != nil { 84 | panic(err) 85 | } 86 | } 87 | break 88 | } 89 | if i > 4 { 90 | break 91 | } 92 | 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /test/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Pani Networks 2 | // All Rights Reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // +build ignore 17 | 18 | // Package test for testing purpose. 19 | package test 20 | --------------------------------------------------------------------------------