Repository: cilium/cilium
Pull request #11022 (2020-04-16 20:33:27)

Small fixes for docker getting-started example
* bump cilium version in Docker getting-started example to 1.7.2
* add bind mount for `/var/lib/cilium` to `docker-compose.yml`

Found while working on a fix for #10962 (separate PR, as this only affects master and doesn't need to be backported)

tklauser: test-me-please
coveralls: 
[![Coverage Status](https://coveralls.io/builds/30130376/badge)](https://coveralls.io/builds/30130376)

Coverage decreased (-0.02%) to 46.784% when pulling **42ca1483ca9d74c502c47d3e7f5ddc577012a029 on pr/tklauser/docker-gsg** into **8b74e24a920391c9c49564df41e49e49f7edabe4 on master**.


Patch:
diff --git a/.dockerignore b/.dockerignore
index e21d69008b..0f2c34f66b 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -38,7 +38,7 @@ coverage-all.html
 .idea/
 *.plist
 
-cilium/bash_autocomplete
+*_bash_completion
 *.swo
 outgoing
 
diff --git a/.gitignore b/.gitignore
index 0622bd019a..394b1d9d2e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,7 +40,7 @@ coverage-all.html
 .idea/
 *.plist
 
-cilium/bash_autocomplete
+*_bash_completion
 *.swo
 outgoing
 
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 25f879de95..5f4f893df7 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -1,3 +1,6 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../Makefile.quiet
 
 .PHONY: default clean builder-image cmdref epub latex html run-server stop-server
diff --git a/Documentation/install/upgrade.rst b/Documentation/install/upgrade.rst
index b9d0d5dec5..d53ccb5563 100644
--- a/Documentation/install/upgrade.rst
+++ b/Documentation/install/upgrade.rst
@@ -639,13 +639,13 @@ New ConfigMap Options
     will only automatically allow connectivity from the local node, thereby providing
     a better default security posture.
 
-    The option is enabled by
-    default for new deployments when generated via Helm, in order to gain the benefits
-    of improved security. The option can be disabled
-    in order to maintain full compatibility with Cilium 1.6.x policy enforcement.
-    **Be aware** that upgrading a cluster to 1.7.x by using helm to generate a new Cilium config
-    that leaves ``enable-remote-node-identity`` set as the default value
-    of ``true`` **can break network connectivity.**
+    The option is enabled by default for new deployments when generated via
+    Helm, in order to gain the benefits of improved security. The Helm option
+    is ``--set global.remoteNodeIdentity``. This option can be disabled in
+    order to maintain full compatibility with Cilium 1.6.x policy enforcement.
+    **Be aware** that upgrading a cluster to 1.7.x by using helm to generate a
+    new Cilium config that leaves ``enable-remote-node-identity`` set as the
+    default value of ``true`` **can break network connectivity.**
 
     The reason for this is that
     with Cilium 1.6.x, the source identity of ANY connection from a host-networking pod or from
@@ -663,16 +663,16 @@ New ConfigMap Options
     Cilium monitor with a source identity equal to 6 (the numeric value for the
     new ``remote-node`` identity.   For example:
 
+    ::
 
-    .. parsed-literal::
-
-      xx drop (Policy denied) flow 0x6d7b6dd0 to endpoint 1657, identity 6->51566: 172.16.9.243:47278 -> 172.16.8.21:9093 tcp SYN
-
+       xx drop (Policy denied) flow 0x6d7b6dd0 to endpoint 1657, identity 6->51566: 172.16.9.243:47278 -> 172.16.8.21:9093 tcp SYN
 
-    There are two ways to address this.  One can set ``enable-remote-node-identity=false``
-    to retain the Cilium 1.6.x behavior.  However, this is not ideal, as it means there is
-    no way to prevent communication between host-networking pods and Cilium-managed pods,
-    since all such connectivity is allowed automatically because it is from the ``host`` identity.
+    There are two ways to address this.  One can set
+    ``enable-remote-node-identity=false`` in the `ConfigMap` to retain the
+    Cilium 1.6.x behavior.  However, this is not ideal, as it means there is no
+    way to prevent communication between host-networking pods and
+    Cilium-managed pods, since all such connectivity is allowed automatically
+    because it is from the ``host`` identity.
 
     The other option is to keep ``enable-remote-node-identity=true`` and
     create policy rules that explicitly whitelist connections between
@@ -681,19 +681,21 @@ New ConfigMap Options
     such a rule is:
 
 
-    .. parsed-literal::
+    ::
+
+       apiVersion: "cilium.io/v2"
+       kind: CiliumNetworkPolicy
+       metadata:
+         name: "allow-from-remote-nodes"
+       spec:
+         endpointSelector:
+           matchLabels:
+             app: myapp
+         ingress:
+         - fromEntities:
+           - remote-node
 
-      apiVersion: "cilium.io/v2"
-      kind: CiliumNetworkPolicy
-      metadata:
-        name: "allow-from-remote-nodes"
-      spec:
-        endpointSelector:
-          matchLabels:
-            app: myapp
-        ingress:
-        - fromEntities:
-          - remote-node
+    See :ref:`policy-remote-node` for more examples of remote-node policies.
 
 
   * ``enable-well-known-identities`` has been added to control the
diff --git a/Documentation/policy/language.rst b/Documentation/policy/language.rst
index 8831872960..dc77cd99ae 100644
--- a/Documentation/policy/language.rst
+++ b/Documentation/policy/language.rst
@@ -327,7 +327,7 @@ Access to/from local host
 Allow all endpoints with the label ``env=dev`` to access the host that is
 serving the particular endpoint.
 
-.. note:: Kubernetes will automatically allow all communication from and to the
+.. note:: Kubernetes will automatically allow all communication from the
 	  local host of all local endpoints. You can run the agent with the
 	  option ``--allow-localhost=policy`` to disable this behavior which
 	  will give you control over this via policy.
@@ -346,6 +346,27 @@ serving the particular endpoint.
 
         .. literalinclude:: ../../examples/policies/l3/entities/host.json
 
+.. _policy-remote-node:
+
+Access to/from all nodes in the cluster
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Allow all endpoints with the label ``env=dev`` to receive traffic from any host
+in the cluster that Cilium is running on.
+
+.. only:: html
+
+   .. tabs::
+     .. group-tab:: k8s YAML
+
+        .. literalinclude:: ../../examples/policies/l3/entities/nodes.yaml
+     .. group-tab:: JSON
+
+        .. literalinclude:: ../../examples/policies/l3/entities/nodes.json
+
+.. only:: epub or latex
+
+        .. literalinclude:: ../../examples/policies/l3/entities/nodes.json
 
 Access to/from outside cluster
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Makefile b/Makefile
index 951b486c5a..64cb843105 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,6 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include Makefile.defs
 
 SUBDIRS_CILIUM_CONTAINER := proxylib envoy plugins/cilium-cni bpf cilium daemon cilium-health bugtool
@@ -5,9 +8,9 @@ ifdef LIBNETWORK_PLUGIN
 SUBDIRS_CILIUM_CONTAINER += plugins/cilium-docker
 endif
 SUBDIRS := $(SUBDIRS_CILIUM_CONTAINER) operator plugins tools hubble-proxy
-GOFILES_EVAL := $(subst _$(ROOT_DIR)/,,$(shell $(CGO_DISABLED) $(GOLIST) $(GO) list -e ./...))
+GOFILES_EVAL := $(subst _$(ROOT_DIR)/,,$(shell $(GO_LIST) -e ./...))
 GOFILES ?= $(GOFILES_EVAL)
-TESTPKGS_EVAL := $(subst github.com/cilium/cilium/,,$(shell $(CGO_DISABLED) $(GOLIST) $(GO) list -e ./... | grep -v '/api/v1\|/vendor\|/contrib' | grep -v -P 'test(?!/helpers/logutils)'))
+TESTPKGS_EVAL := $(subst github.com/cilium/cilium/,,$(shell $(GO_LIST) -e ./... | grep -v '/api/v1\|/vendor\|/contrib' | grep -v -P 'test(?!/helpers/logutils)'))
 TESTPKGS ?= $(TESTPKGS_EVAL)
 GOLANGVERSION := $(shell $(GO) version 2>/dev/null | grep -Eo '(go[0-9].[0-9])')
 GOLANG_SRCFILES := $(shell for pkg in $(subst github.com/cilium/cilium/,,$(GOFILES)); do find $$pkg -name *.go -print; done | grep -v vendor | sort | uniq)
@@ -89,7 +92,7 @@ tests-privileged:
 	# cilium-map-migrate is a dependency of some unit tests.
 	$(QUIET) $(MAKE) $(SUBMAKEOPTS) -C bpf cilium-map-migrate
 	$(QUIET)$(foreach pkg,$(PRIV_TEST_PKGS),\
-		$(GO) test $(GOFLAGS) $(TEST_LDFLAGS) github.com/cilium/cilium/$(pkg) $(GOTEST_PRIV_OPTS) || exit 1;)
+		$(GO_TEST) $(TEST_LDFLAGS) github.com/cilium/cilium/$(pkg) $(GOTEST_PRIV_OPTS) || exit 1;)
 
 start-kvstores:
 ifeq ($(SKIP_KVSTORES),"false")
@@ -151,7 +154,7 @@ endif
 	# hence will trigger an error of too many arguments. As a workaround, we
 	# have to process these packages in different subshells.
 	for pkg in $(patsubst %,github.com/cilium/cilium/%,$(TESTPKGS)); do \
-		$(_GO) test $(GOFLAGS) $(TEST_UNITTEST_LDFLAGS) $$pkg $(GOTEST_BASE) $(GOTEST_COVER_OPTS) || exit 1; \
+		$(GO_TEST) $(TEST_UNITTEST_LDFLAGS) $$pkg $(GOTEST_BASE) $(GOTEST_COVER_OPTS) || exit 1; \
 		tail -n +2 coverage.out >> coverage-all-tmp.out; \
 	done
 	$(MAKE) generate-cov
@@ -160,14 +163,14 @@ endif
 
 bench: start-kvstores
 	$(QUIET)$(foreach pkg,$(patsubst %,github.com/cilium/cilium/%,$(TESTPKGS)),\
-		$(GO) test $(GOFLAGS) $(TEST_UNITTEST_LDFLAGS) $(GOTEST_BASE) $(BENCHFLAGS) \
+		$(GO_TEST) $(TEST_UNITTEST_LDFLAGS) $(GOTEST_BASE) $(BENCHFLAGS) \
 			$(pkg) \
 		|| exit 1;)
 	$(MAKE) stop-kvstores
 
 bench-privileged:
 	$(QUIET)$(foreach pkg,$(patsubst %,github.com/cilium/cilium/%,$(TESTPKGS)),\
-		$(GO) test $(GOFLAGS) $(TEST_UNITTEST_LDFLAGS) $(GOTEST_BASE) $(BENCHFLAGS) \
+		$(GO_TEST) $(TEST_UNITTEST_LDFLAGS) $(GOTEST_BASE) $(BENCHFLAGS) \
 			-tags=privileged_tests $(pkg) \
 		|| exit 1;)
 
@@ -365,7 +368,7 @@ gofmt:
 
 govet:
 	@$(ECHO_CHECK) vetting all GOFILES...
-	$(QUIET) $(GOLIST) $(_GO) vet \
+	$(QUIET) $(GO_VET) \
     ./api/... \
     ./bugtool/... \
     ./cilium/... \
diff --git a/Makefile.defs b/Makefile.defs
index 41ab42d518..181de58e11 100644
--- a/Makefile.defs
+++ b/Makefile.defs
@@ -1,3 +1,6 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 SHELL := /bin/bash
 .SHELLFLAGS := -eu -o pipefail -c
 
@@ -14,16 +17,32 @@ LOCALSTATEDIR?=/var
 RUNDIR?=/var/run
 CONFDIR?=/etc
 
-CGO_DISABLED = CGO_ENABLED=0
-GOLIST ?= GO111MODULE=off
-ifeq ($(GO),)
-	GO = $(CGO_DISABLED) go
-	_GO = go
+INSTALL = install
+
+# Container engine
+export CONTAINER_ENGINE?=docker
+CONTAINER_ENGINE_FULL=$(QUIET)$(CONTAINER_ENGINE)
+
+# Set DOCKER_IMAGE_TAG with "latest" by default
+ifeq ($(DOCKER_IMAGE_TAG),)
+    DOCKER_IMAGE_TAG="latest"
+endif
+
+ifeq ($(shell uname -m),aarch64)
+    ETCD_IMAGE=quay.io/coreos/etcd:v3.2.17-arm64
 else
-	_GO = $(GO)
+    ETCD_IMAGE=quay.io/coreos/etcd:v3.2.17
 endif
 
-INSTALL = install
+GO ?= go
+
+# go build/test/clean flags
+# these are declared here so they are treated explicitly
+# as non-immediate variables
+GO_BUILD_FLAGS = 
+GO_TEST_FLAGS = 
+GO_CLEAN_FLAGS = 
+GO_BUILD_LDFLAGS = 
 
 VERSION = $(shell cat $(dir $(lastword $(MAKEFILE_LIST)))/VERSION)
 VERSION_MAJOR = $(shell cat $(dir $(lastword $(MAKEFILE_LIST)))/VERSION | cut -d. -f1)
@@ -33,8 +52,8 @@ ifneq ($(wildcard $(dir $(lastword $(MAKEFILE_LIST)))/.git),)
 else
 	GIT_VERSION = $(shell cat $(ROOT_DIR)/GIT_VERSION)
 endif
-BUILD = $(VERSION) $(GIT_VERSION) $(shell $(GO) version)
-GOLDFLAGS = -X "github.com/cilium/cilium/pkg/version.Version=$(BUILD)"
+FULL_BUILD_VERSION = $(VERSION) $(GIT_VERSION) $(shell $(GO) version)
+GO_BUILD_LDFLAGS += -X "github.com/cilium/cilium/pkg/version.Version=$(FULL_BUILD_VERSION)"
 
 ifeq ($(NOSTRIP),)
 	# Note: these options will not remove annotations needed for stack
@@ -42,23 +61,18 @@ ifeq ($(NOSTRIP),)
 	#
 	# -w: Omit the DWARF symbol table.
 	# -s: Omit the symbol table and debug information.
-	GOLDFLAGS += -s -w
+	GO_BUILD_LDFLAGS += -s -w
 endif
 
 CILIUM_ENVOY_SHA=$(shell grep -o "FROM.*cilium/cilium-envoy:[0-9a-fA-F]*" $(ROOT_DIR)/Dockerfile | cut -d : -f 2)
-GOLDFLAGS += -X "github.com/cilium/cilium/pkg/envoy.RequiredEnvoyVersionSHA=$(CILIUM_ENVOY_SHA)"
+GO_BUILD_LDFLAGS += -X "github.com/cilium/cilium/pkg/envoy.RequiredEnvoyVersionSHA=$(CILIUM_ENVOY_SHA)"
 
 BPF_FILES_EVAL := $(shell git ls-files $(ROOT_DIR)/bpf/ | grep -v .gitignore | tr "\n" ' ')
 BPF_FILES ?= $(BPF_FILES_EVAL)
 BPF_SRCFILES := $(subst ../,,$(BPF_FILES))
 
 CILIUM_DATAPATH_SHA=$(shell cat $(BPF_FILES) | sha1sum | awk '{print $$1}')
-GOLDFLAGS += -X "github.com/cilium/cilium/pkg/datapath/loader.DatapathSHA=$(CILIUM_DATAPATH_SHA)"
-
-# Set DOCKER_IMAGE_TAG with "latest" by default
-ifeq ($(DOCKER_IMAGE_TAG),)
-    DOCKER_IMAGE_TAG="latest"
-endif
+GO_BUILD_LDFLAGS += -X "github.com/cilium/cilium/pkg/datapath/loader.DatapathSHA=$(CILIUM_DATAPATH_SHA)"
 
 # Set -mod=vendor if running >= go 1.13 or if GO111MODULE is set.
 # A go build is being executed with go modules if:
@@ -66,37 +80,37 @@ endif
 # * The go command is invoked in a directory outside of the $GOPATH/src tree
 #   and the environment variable GO111MODULE unset (or explicitly set to 'auto').
 ifeq ($(GO111MODULE),on)
-    GOFLAGS ?= -mod=vendor
-    GOCLEAN ?= -mod=vendor
+    GO_BUILD_FLAGS += -mod=vendor
+    GO_TEST_FLAGS += -mod=vendor
+    GO_CLEAN_FLAGS += -mod=vendor
 endif
+
 # Compile with '-mod=vendor' if go >= 1.13
 GO_MAJOR_VERSION_GE_1 := $(shell expr `$(GO) version | grep -E 'go[0-9]{1}+' -o | sed 's/go//g'` \>= 1)
 ifeq ($(GO_MAJOR_VERSION_GE_1),1)
     GO_MINOR_VERSION_GE_13 := $(shell expr `$(GO) version | grep -E 'go[^ ]+' -o | sed 's/go1.//g'` \>= 13)
     ifeq ($(GO_MINOR_VERSION_GE_13),1)
-        GOFLAGS ?= -mod=vendor
-        GOCLEAN ?= -mod=vendor
+        GO_BUILD_FLAGS += -mod=vendor
+        GO_TEST_FLAGS += -mod=vendor
+        GO_CLEAN_FLAGS += -mod=vendor
     endif
 endif
 
-GOBUILD = $(GOFLAGS) -ldflags '$(GOLDFLAGS)' $(EXTRA_GOBUILD_FLAGS)
-
-# Uncomment to enable race detection
-#GOBUILD += -race
-
-# Uncomment to enable deadlock detection
-#GOBUILD += -tags lockdebug
+ifneq ($(RACE),)
+    GO_BUILD_FLAGS += -race
+    GO_TEST_FLAGS += -race
+endif
 
 ifneq ($(LOCKDEBUG),)
-    GOBUILD += -tags lockdebug
+    GO_BUILD_FLAGS += -tags lockdebug
 endif
 
-# Container engine
-export CONTAINER_ENGINE?=docker
-CONTAINER_ENGINE_FULL=$(QUIET)$(CONTAINER_ENGINE)
+GO_BUILD_FLAGS += -ldflags '$(GO_BUILD_LDFLAGS) $(EXTRA_GO_BUILD_LDFLAGS)' $(EXTRA_GO_BUILD_FLAGS)
 
-ifeq ($(shell uname -m),aarch64)
-    ETCD_IMAGE=quay.io/coreos/etcd:v3.2.17-arm64
-else
-    ETCD_IMAGE=quay.io/coreos/etcd:v3.2.17
-endif
+GO_BUILD = CGO_ENABLED=0 $(GO) build $(GO_BUILD_FLAGS)
+GO_BUILD_WITH_CGO = CGO_ENABLED=1 $(GO) build $(GO_BUILD_FLAGS)
+GO_TEST = $(GO) test $(GO_TEST_FLAGS)
+GO_CLEAN = $(GO) clean $(GO_TEST_FLAGS)
+# TODO: remove `GO111MODULE=off` once Go 1.13 is deprecated by Go maintainers
+GO_VET = GO111MODULE=off $(GO) vet
+GO_LIST = GO111MODULE=off $(GO) list
diff --git a/Makefile.quiet b/Makefile.quiet
index e606d68a23..1a78ba4ee8 100644
--- a/Makefile.quiet
+++ b/Makefile.quiet
@@ -1,3 +1,6 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 ifeq ($(ROOT_DIR),)
 	ROOT_DIR ?= $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
 	RELATIVE_DIR ?= $(shell echo $(realpath .) | sed "s;$(ROOT_DIR)[/]*;;")
diff --git a/api/v1/Makefile b/api/v1/Makefile
index 0c69aba610..89affb67d9 100644
--- a/api/v1/Makefile
+++ b/api/v1/Makefile
@@ -1,3 +1,6 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 PROTOC ?= protoc
 
 HUBBLE_PROTO_SOURCES := \
diff --git a/bpf/Makefile b/bpf/Makefile
index 1d57dd08e6..9cc943f7ae 100644
--- a/bpf/Makefile
+++ b/bpf/Makefile
@@ -1,3 +1,6 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../Makefile.defs
 
 .PHONY: all subdirs check preprocess assembly install clean
diff --git a/bpf/Makefile.bpf b/bpf/Makefile.bpf
index 1412030b2b..292d7e86c6 100644
--- a/bpf/Makefile.bpf
+++ b/bpf/Makefile.bpf
@@ -1,3 +1,6 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 FLAGS := -I$(ROOT_DIR)/bpf/include -I$(ROOT_DIR)/bpf -D__NR_CPUS__=$(shell nproc) -O2 -g
 
 CLANG_FLAGS := ${FLAGS} -target bpf -emit-llvm
diff --git a/bpf/sockops/Makefile b/bpf/sockops/Makefile
index 19d9ad40e4..4697445884 100644
--- a/bpf/sockops/Makefile
+++ b/bpf/sockops/Makefile
@@ -1,3 +1,6 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../../Makefile.defs
 
 .PHONY: all assembly check preprocess clean
diff --git a/bugtool/Makefile b/bugtool/Makefile
index 9f5fcc72c8..91508f97f1 100644
--- a/bugtool/Makefile
+++ b/bugtool/Makefile
@@ -1,31 +1,22 @@
 # Copyright 2017-2020 Authors of Cilium
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# SPDX-License-Identifier: Apache-2.0
 
 include ../Makefile.defs
 
-TARGET=cilium-bugtool
-SOURCES := $(shell find ../common . -name '*.go')
-$(TARGET): $(SOURCES)
-	@$(ECHO_GO)
-	$(QUIET)$(GO) build $(GOBUILD) -o $(TARGET)
+TARGET := cilium-bugtool
+
+.PHONY: all $(TARGET) clean install
 
 all: $(TARGET)
 
+$(TARGET):
+	@$(ECHO_GO)
+	$(QUIET)$(GO_BUILD) -o $@
+
 clean:
 	@$(ECHO_CLEAN)
 	-$(QUIET)rm -f .cilium-bugtool.config $(TARGET)
-	$(QUIET)$(GO) clean $(GOCLEAN)
+	$(QUIET)$(GO_CLEAN)
 
 install:
 	$(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR)
diff --git a/cilium-dev.Dockerfile.dockerignore b/cilium-dev.Dockerfile.dockerignore
index 76952e1e91..266b986614 100644
--- a/cilium-dev.Dockerfile.dockerignore
+++ b/cilium-dev.Dockerfile.dockerignore
@@ -45,7 +45,7 @@ coverage-all.html
 .idea/
 *.plist
 
-cilium/bash_autocomplete
+*_bash_completion
 *.swo
 outgoing
 
diff --git a/cilium-docker-plugin.Dockerfile b/cilium-docker-plugin.Dockerfile
index daf7d6fb6b..fa4a4455d0 100644
--- a/cilium-docker-plugin.Dockerfile
+++ b/cilium-docker-plugin.Dockerfile
@@ -3,8 +3,7 @@ LABEL maintainer="maintainer@cilium.io"
 ADD . /go/src/github.com/cilium/cilium
 WORKDIR /go/src/github.com/cilium/cilium/plugins/cilium-docker
 ARG LOCKDEBUG
-ARG V
-RUN make CGO_ENABLED=0 GOOS=linux LOCKDEBUG=$LOCKDEBUG PKG_BUILD=1 EXTRA_GOBUILD_FLAGS="-a -installsuffix cgo"
+RUN make LOCKDEBUG=$LOCKDEBUG
 
 FROM scratch
 LABEL maintainer="maintainer@cilium.io"
diff --git a/cilium-health/Makefile b/cilium-health/Makefile
index 29287f617d..b1ffd5dbed 100644
--- a/cilium-health/Makefile
+++ b/cilium-health/Makefile
@@ -1,31 +1,33 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../Makefile.defs
 
-SUBDIRS = responder
-TARGET = cilium-health
+TARGET := cilium-health
+SUBDIRS := responder
 
-.PHONY: all $(SUBDIRS) install clean
+.PHONY: all $(TARGET) $(SUBDIRS) clean install
 
 all: $(TARGET) $(SUBDIRS)
 
-SOURCES := $(shell find ../api/v1/health ../pkg/health cmd . \
-	\( -name '*.go' ! -name '*_test.go' $(foreach dir,$(SUBDIRS),! -path './$(dir)/*') \) )
-$(TARGET): $(SOURCES)
+$(TARGET):
 	@$(ECHO_GO)
-	$(QUIET)$(GO) build $(GOBUILD) -o $(TARGET)
-
-$(SUBDIRS): force
-	@ $(MAKE) $(SUBMAKEOPTS) -C $@ all
+	$(QUIET)$(GO_BUILD) -o $@
 
 clean:
 	@$(ECHO_CLEAN)
 	$(QUIET)for i in $(SUBDIRS); do $(MAKE) $(SUBMAKEOPTS) -C $$i clean; done
 	-$(QUIET)rm -f $(TARGET)
-	$(QUIET)$(GO) clean $(GOCLEAN)
+	$(QUIET)$(GO_CLEAN)
 
 install:
 	$(QUIET)for i in $(SUBDIRS); do $(MAKE) $(SUBMAKEOPTS) -C $$i install; done
 	$(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR)
 	$(QUIET)$(INSTALL) -m 0755 $(TARGET) $(DESTDIR)$(BINDIR)
 
+
+$(SUBDIRS): force
+	@ $(MAKE) $(SUBMAKEOPTS) -C $@ all
+
 .PHONY: force
 force :;
diff --git a/cilium-health/responder/Makefile b/cilium-health/responder/Makefile
index 050a883f59..29c25ab5cf 100644
--- a/cilium-health/responder/Makefile
+++ b/cilium-health/responder/Makefile
@@ -1,17 +1,22 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../../Makefile.defs
 
-TARGET=cilium-health-responder
-SOURCES := $(shell find ../../pkg/health/probe/responder . \( -name '*.go' ! -name '*_test.go' \))
-$(TARGET): $(SOURCES)
-	@$(ECHO_GO)
-	$(QUIET)$(GO) build $(GOBUILD) -o $(TARGET)
+TARGET := cilium-health-responder
+
+.PHONY: all $(TARGET) clean install
 
 all: $(TARGET)
 
+$(TARGET):
+	@$(ECHO_GO)
+	$(QUIET)$(GO_BUILD) -o $@
+
 clean:
 	@$(ECHO_CLEAN)
 	-$(QUIET)rm -f $(TARGET)
-	$(QUIET)$(GO) clean $(GOCLEAN)
+	$(QUIET)$(GO_CLEAN)
 
 install:
 	$(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR)
diff --git a/cilium-operator.Dockerfile b/cilium-operator.Dockerfile
index 3ee408cb30..4995b6a4ea 100644
--- a/cilium-operator.Dockerfile
+++ b/cilium-operator.Dockerfile
@@ -3,8 +3,7 @@ LABEL maintainer="maintainer@cilium.io"
 ADD . /go/src/github.com/cilium/cilium
 WORKDIR /go/src/github.com/cilium/cilium/operator
 ARG LOCKDEBUG
-ARG V
-RUN make CGO_ENABLED=0 GOOS=linux LOCKDEBUG=$LOCKDEBUG PKG_BUILD=1 EXTRA_GOBUILD_FLAGS="-a -installsuffix cgo -tags operator_aws,operator_azure"
+RUN make LOCKDEBUG=$LOCKDEBUG EXTRA_GO_BUILD_FLAGS="-tags operator_aws,operator_azure"
 
 FROM docker.io/library/alpine:3.9.3 as certs
 RUN apk --update add ca-certificates
diff --git a/cilium/.gitignore b/cilium/.gitignore
index f3c6bfa6a3..700f27eb0f 100644
--- a/cilium/.gitignore
+++ b/cilium/.gitignore
@@ -1,2 +1,1 @@
 cilium
-bash_autocomplete
diff --git a/cilium/Makefile b/cilium/Makefile
index 845a21a0a6..918a453b45 100644
--- a/cilium/Makefile
+++ b/cilium/Makefile
@@ -1,23 +1,26 @@
-include ../Makefile.quiet
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../Makefile.defs
 
-TARGET=cilium
-SOURCES := $(shell find ../api ../daemon ../common ../pkg cmd . \( -name '*.go' ! -name '*_test.go' \))
+TARGET := cilium
 
-$(TARGET): $(SOURCES)
-	@$(ECHO_GO)
-	$(QUIET)$(GO) build $(GOBUILD) -o $(TARGET)
+.PHONY: all $(TARGET) clean install
 
 all: $(TARGET)
 
+$(TARGET):
+	@$(ECHO_GO)
+	$(QUIET)$(GO_BUILD) -o $@
+
 clean:
-	@$(ECHO_CLEAN) $(notdir $(shell pwd))
+	@$(ECHO_CLEAN)
 	-$(QUIET)rm -f $(TARGET)
-	$(QUIET)$(GO) clean $(GOCLEAN)
+	$(QUIET)$(GO_CLEAN)
 
 install:
 	$(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR)
 	$(QUIET)$(INSTALL) -m 0755 $(TARGET) $(DESTDIR)$(BINDIR)
 	$(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(CONFDIR)/bash_completion.d
-	./$(TARGET) completion bash > bash_autocomplete
-	$(QUIET)$(INSTALL) -m 0644 -T bash_autocomplete $(DESTDIR)$(CONFDIR)/bash_completion.d/cilium
+	./$(TARGET) completion bash > $(TARGET)_bash_completion
+	$(QUIET)$(INSTALL) -m 0644 -T $(TARGET)_bash_completion $(DESTDIR)$(CONFDIR)/bash_completion.d/$(TARGET)
diff --git a/contrib/codegen/Makefile b/contrib/codegen/Makefile
index cca0cc9eab..a3b0a2059c 100644
--- a/contrib/codegen/Makefile
+++ b/contrib/codegen/Makefile
@@ -1,3 +1,6 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 CFLAGS=-O2 -Wall
 
 all: hexgen
diff --git a/contrib/packaging/deb/Makefile b/contrib/packaging/deb/Makefile
index b0b5020f0a..45e8c273a0 100644
--- a/contrib/packaging/deb/Makefile
+++ b/contrib/packaging/deb/Makefile
@@ -1,3 +1,6 @@
+# Copyright 2017-2019 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../../../Makefile.defs
 
 BUILDDIR := "$(CURDIR)/cilium-$(VERSION)"
@@ -5,7 +8,6 @@ BASEDIR := "$(CURDIR)/../../../"
 BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
 
 build: clean
-
 	mkdir -p output
 	(cd $(BASEDIR) && git bundle create $(CURDIR)/version_$(VERSION) $(BRANCH) --tags)
 	(cd $(CURDIR) && git clone $(CURDIR)/version_$(VERSION) cilium -b $(BRANCH))
diff --git a/contrib/packaging/rpm/Makefile b/contrib/packaging/rpm/Makefile
index 7cae4143d8..cd9877de68 100644
--- a/contrib/packaging/rpm/Makefile
+++ b/contrib/packaging/rpm/Makefile
@@ -1,3 +1,6 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../../../Makefile.defs
 
 VERSION := $(shell git tag -l --sort=-v:refname | sed 's/v\([^-].*\)/\1/g' | head -1)
diff --git a/daemon/Makefile b/daemon/Makefile
index 0fc23db04e..f8e25cee18 100644
--- a/daemon/Makefile
+++ b/daemon/Makefile
@@ -1,22 +1,26 @@
-# GOBUILD relies on the order of makefile list to get VERSION file
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../Makefile.defs
 
-TARGET=cilium-agent
-LINKS=cilium-node-monitor
-SOURCES := $(shell find ../api ../common ../daemon ../pkg . \( -name '*.go'  ! -name '*_test.go' \))
-$(TARGET): $(SOURCES) ../Dockerfile ../Makefile ../Makefile.defs Makefile
-	@$(ECHO_GO)
-	$(QUIET)$(GO) build $(GOBUILD) -o $(TARGET)
+TARGET := cilium-agent
+LINKS := cilium-node-monitor
 
 all: $(TARGET) links
 
+.PHONY: all $(TARGET) links
+
+$(TARGET): ../Dockerfile ../Makefile ../Makefile.defs Makefile
+	@$(ECHO_GO)
+	$(QUIET)$(GO_BUILD) -o $(TARGET)
+
 links:
 	$(foreach link,$(LINKS), ln -f -s $(TARGET) $(link) || cp $(TARGET) $(link);)
 
 clean:
 	@$(ECHO_CLEAN)
 	$(QUIET)rm -f $(TARGET)
-	$(QUIET)$(GO) clean $(GOCLEAN)
+	$(QUIET)$(GO_CLEAN)
 	$(foreach link,$(LINKS), $(QUIET)rm -f $(link);)
 
 ifeq ("$(PKG_BUILD)","")
diff --git a/daemon/cmd/daemon.go b/daemon/cmd/daemon.go
index 9aa8532f5f..d96257fe07 100644
--- a/daemon/cmd/daemon.go
+++ b/daemon/cmd/daemon.go
@@ -397,7 +397,8 @@ func NewDaemon(ctx context.Context, dp datapath.Datapath) (*Daemon, *endpointRes
 	k8s.Configure(option.Config.K8sAPIServer, option.Config.K8sKubeConfigPath, defaults.K8sClientQPSLimit, defaults.K8sClientBurst)
 	bootstrapStats.k8sInit.End(true)
 	d.k8sWatcher.RunK8sServiceHandler()
-	policyApi.InitEntities(option.Config.ClusterName)
+	treatRemoteNodeAsHost := option.Config.AlwaysAllowLocalhost() && !option.Config.EnableRemoteNodeIdentity
+	policyApi.InitEntities(option.Config.ClusterName, treatRemoteNodeAsHost)
 
 	bootstrapStats.cleanup.Start()
 	err = clearCiliumVeths()
diff --git a/examples/getting-started/Vagrantfile b/examples/getting-started/Vagrantfile
index 37a87ed7ad..b2b572f247 100644
--- a/examples/getting-started/Vagrantfile
+++ b/examples/getting-started/Vagrantfile
@@ -3,14 +3,14 @@
 
 Vagrant.require_version ">= 2.0.0"
 
-cilium_version = (ENV['CILIUM_VERSION'] || "v1.7.0")
+cilium_version = (ENV['CILIUM_VERSION'] || "v1.7.2")
 cilium_opts = (ENV['CILIUM_OPTS'] || "--enable-ipv6=false --kvstore consul --kvstore-opt consul.address=127.0.0.1:8500 -t vxlan")
-cilium_tag = (ENV['CILIUM_TAG'] || "v1.7.0")
+cilium_tag = (ENV['CILIUM_TAG'] || "v1.7.2")
 
 # This runs only once when vagrant box is provisioned for the first time
 $bootstrap = <<SCRIPT
 # install docker-compose
-curl -sS -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
+curl -sS -L "https://github.com/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
 chmod +x /usr/local/bin/docker-compose
 
 # install cilium client
diff --git a/examples/getting-started/docker-compose.yml b/examples/getting-started/docker-compose.yml
index 11fd4639bf..cc204be8b5 100644
--- a/examples/getting-started/docker-compose.yml
+++ b/examples/getting-started/docker-compose.yml
@@ -7,6 +7,7 @@ services:
     volumes:
       - /var/run/docker.sock:/var/run/docker.sock
       - /var/run/cilium:/var/run/cilium
+      - /var/lib/cilium:/var/lib/cilium
       - /sys/fs/bpf:/sys/fs/bpf
       # To access Docker container netns:
       - /var/run/docker/netns:/var/run/docker/netns:rshared
diff --git a/examples/kubernetes/addons/prometheus/templates/Makefile b/examples/kubernetes/addons/prometheus/templates/Makefile
index c3f243e219..e3e38343b5 100644
--- a/examples/kubernetes/addons/prometheus/templates/Makefile
+++ b/examples/kubernetes/addons/prometheus/templates/Makefile
@@ -1,5 +1,7 @@
-include ../../../../../Makefile.defs
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
 
+include ../../../../../Makefile.defs
 
 DESTINATION= "../monitoring-example.yaml"
 
diff --git a/examples/kubernetes/connectivity-check/Makefile b/examples/kubernetes/connectivity-check/Makefile
index a1f8ed28d2..48340af61f 100644
--- a/examples/kubernetes/connectivity-check/Makefile
+++ b/examples/kubernetes/connectivity-check/Makefile
@@ -1,3 +1,6 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 OUT := connectivity-check.yaml
 SRC := $(wildcard *.yaml)
 SRC := $(filter-out $(OUT), $(SRC))
diff --git a/examples/policies/l3/entities/nodes.json b/examples/policies/l3/entities/nodes.json
new file mode 100644
index 0000000000..0b670d264f
--- /dev/null
+++ b/examples/policies/l3/entities/nodes.json
@@ -0,0 +1,10 @@
+[{
+    "labels": [{"key": "name", "value": "to-dev-from-nodes-in-cluster"}],
+    "endpointSelector": {"matchLabels": {"env":"dev"}},
+    "ingress": [{
+        "fromEntities": [
+            "host",
+            "remote-node"
+        ]
+    }]
+}]
diff --git a/examples/policies/l3/entities/nodes.yaml b/examples/policies/l3/entities/nodes.yaml
new file mode 100644
index 0000000000..f4144d8730
--- /dev/null
+++ b/examples/policies/l3/entities/nodes.yaml
@@ -0,0 +1,12 @@
+apiVersion: "cilium.io/v2"
+kind: CiliumNetworkPolicy
+metadata:
+  name: "to-dev-from-nodes-in-cluster"
+spec:
+  endpointSelector:
+    matchLabels:
+      env: dev
+  ingress:
+    - fromEntities:
+      - host
+      - remote-node
diff --git a/hubble-proxy/.gitignore b/hubble-proxy/.gitignore
index d32972278f..f69665e491 100644
--- a/hubble-proxy/.gitignore
+++ b/hubble-proxy/.gitignore
@@ -1,2 +1,1 @@
 hubble-proxy
-bash_autocomplete
diff --git a/hubble-proxy/Makefile b/hubble-proxy/Makefile
index 4bfe6be7fd..ae4d207d65 100644
--- a/hubble-proxy/Makefile
+++ b/hubble-proxy/Makefile
@@ -1,27 +1,27 @@
-include ../Makefile.quiet
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../Makefile.defs
 
-TARGET=hubble-proxy
-SOURCES := $(shell find . ../pkg/hubble \( -name '*.go' ! -name '*_test.go' \))
+TARGET := hubble-proxy
 
+.PHONY: all $(TARGET) clean install
 
 all: $(TARGET)
 
-$(TARGET): $(SOURCES)
+$(TARGET):
 	@$(ECHO_GO)
-	$(QUIET)$(GO) build $(GOBUILD) -o $(TARGET)
+	$(QUIET)$(GO_BUILD) -o $@
 	strip $(TARGET)
 
 clean:
-	@$(ECHO_CLEAN) $(notdir $(shell pwd))
+	@$(ECHO_CLEAN)
 	-$(QUIET)rm -f $(TARGET)
-	$(QUIET)$(GO) clean $(GOCLEAN)
+	$(QUIET)$(GO_CLEAN)
 
 install:
 	$(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR)
 	$(QUIET)$(INSTALL) -m 0755 $(TARGET) $(DESTDIR)$(BINDIR)
 	$(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(CONFDIR)/bash_completion.d
-	./$(TARGET) completion bash > bash_autocomplete
-	$(QUIET)$(INSTALL) -m 0644 -T bash_autocomplete $(DESTDIR)$(CONFDIR)/bash_completion.d/hubble-proxy
-
-.PHONY: clean install
+	./$(TARGET) completion bash > $(TARGET)_bash_completion
+	$(QUIET)$(INSTALL) -m 0644 -T $(TARGET)_bash_completion $(DESTDIR)$(CONFDIR)/bash_completion.d/$(TARGET)
diff --git a/install/kubernetes/Makefile b/install/kubernetes/Makefile
index 05ff959dc9..79145e7f88 100644
--- a/install/kubernetes/Makefile
+++ b/install/kubernetes/Makefile
@@ -1,5 +1,7 @@
+# Copyright 2017-2019 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../../Makefile.defs
-include $(ROOT_DIR)/Makefile.quiet
 
 MANAGED_ETCD_VERSION := "v2.0.7"
 
diff --git a/operator/Makefile b/operator/Makefile
index 61337e3818..9e90093eaa 100644
--- a/operator/Makefile
+++ b/operator/Makefile
@@ -1,20 +1,23 @@
-# GOBUILD relies on the order of makefile list to get VERSION file
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../Makefile.defs
 
-TARGET=cilium-operator
-SOURCES := $(shell find ../pkg . \( -name '*.go'  ! -name '*_test.go' \))
-$(TARGET): $(SOURCES)
-	@$(ECHO_GO)
-	$(QUIET)$(GO) build $(GOBUILD) -o $(TARGET)
+TARGET := cilium-operator
+
+.PHONY: all $(TARGET) clean install
 
 all: $(TARGET)
 
+$(TARGET):
+	@$(ECHO_GO)
+	$(QUIET)$(GO_BUILD) -o $@
+
 clean:
 	@$(ECHO_CLEAN)
-	$(QUIET)rm -f $(TARGET)
-	$(GO) clean $(GOCLEAN)
+	-$(QUIET)rm -f $(TARGET)
+	$(QUIET)$(GO_CLEAN)
 
 install:
-	groupadd -f cilium
 	$(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR)
 	$(QUIET)$(INSTALL) -m 0755 $(TARGET) $(DESTDIR)$(BINDIR)
diff --git a/pkg/policy/api/entity.go b/pkg/policy/api/entity.go
index 70ed68df86..cd35a85d37 100644
--- a/pkg/policy/api/entity.go
+++ b/pkg/policy/api/entity.go
@@ -101,7 +101,7 @@ func (s EntitySlice) GetAsEndpointSelectors() EndpointSelectorSlice {
 }
 
 // InitEntities is called to initialize the policy API layer
-func InitEntities(clusterName string) {
+func InitEntities(clusterName string, treatRemoteNodeAsHost bool) {
 	EntitySelectorMapping[EntityCluster] = EndpointSelectorSlice{
 		endpointSelectorHost,
 		endpointSelectorRemoteNode,
@@ -109,4 +109,11 @@ func InitEntities(clusterName string) {
 		endpointSelectorUnmanaged,
 		NewESFromLabels(labels.NewLabel(k8sapi.PolicyLabelCluster, clusterName, labels.LabelSourceK8s)),
 	}
+
+	hostSelectors := make(EndpointSelectorSlice, 0, 2)
+	hostSelectors = append(hostSelectors, endpointSelectorHost)
+	if treatRemoteNodeAsHost {
+		hostSelectors = append(hostSelectors, endpointSelectorRemoteNode)
+	}
+	EntitySelectorMapping[EntityHost] = hostSelectors
 }
diff --git a/pkg/policy/l4.go b/pkg/policy/l4.go
index 64a172a05d..b9a919389d 100644
--- a/pkg/policy/l4.go
+++ b/pkg/policy/l4.go
@@ -566,7 +566,7 @@ func (l4 *L4Filter) attach(ctx PolicyContext, l4Policy *L4Policy) {
 //
 // hostWildcardL7 determines if L7 traffic from Host should be
 // wildcarded (in the relevant daemon mode).
-func createL4IngressFilter(policyCtx PolicyContext, fromEndpoints api.EndpointSelectorSlice, hostWildcardL7 bool, rule api.PortRule, port api.PortProtocol,
+func createL4IngressFilter(policyCtx PolicyContext, fromEndpoints api.EndpointSelectorSlice, hostWildcardL7 []string, rule api.PortRule, port api.PortProtocol,
 	protocol api.L4Proto, ruleLabels labels.LabelArray) (*L4Filter, error) {
 
 	filter, err := createL4Filter(policyCtx, fromEndpoints, rule, port, protocol, ruleLabels, true, nil)
@@ -576,11 +576,13 @@ func createL4IngressFilter(policyCtx PolicyContext, fromEndpoints api.EndpointSe
 
 	// If the filter would apply L7 rules for the Host, when we should accept everything from host,
 	// then wildcard Host at L7.
-	if !rule.Rules.IsEmpty() && hostWildcardL7 {
+	if !rule.Rules.IsEmpty() && len(hostWildcardL7) > 0 {
 		for cs := range filter.L7RulesPerSelector {
 			if cs.Selects(identity.ReservedIdentityHost) {
-				hostSelector := api.ReservedEndpointSelectors[labels.IDNameHost]
-				filter.cacheIdentitySelector(hostSelector, policyCtx.GetSelectorCache())
+				for _, name := range hostWildcardL7 {
+					selector := api.ReservedEndpointSelectors[name]
+					filter.cacheIdentitySelector(selector, policyCtx.GetSelectorCache())
+				}
 			}
 		}
 	}
diff --git a/pkg/policy/mapstate.go b/pkg/policy/mapstate.go
index fc416616e9..9305a11169 100644
--- a/pkg/policy/mapstate.go
+++ b/pkg/policy/mapstate.go
@@ -31,6 +31,11 @@ var (
 		Identity:         identity.ReservedIdentityHost.Uint32(),
 		TrafficDirection: trafficdirection.Ingress.Uint8(),
 	}
+	// localRemoteNodeKey represents an ingress L3 allow from remote nodes.
+	localRemoteNodeKey = Key{
+		Identity:         identity.ReservedIdentityRemoteNode.Uint32(),
+		TrafficDirection: trafficdirection.Ingress.Uint8(),
+	}
 )
 
 const (
@@ -140,6 +145,9 @@ func (keys MapState) DetermineAllowLocalhostIngress(l4Policy *L4Policy) {
 			},
 		}
 		keys[localHostKey] = NewMapStateEntry(derivedFrom, false)
+		if !option.Config.EnableRemoteNodeIdentity {
+			keys[localRemoteNodeKey] = NewMapStateEntry(derivedFrom, false)
+		}
 	}
 }
 
diff --git a/pkg/policy/rule.go b/pkg/policy/rule.go
index 3ab3122179..b8a5796fe7 100644
--- a/pkg/policy/rule.go
+++ b/pkg/policy/rule.go
@@ -232,12 +232,12 @@ func mergePortProto(ctx *SearchContext, existingFilter, filterToMerge *L4Filter,
 // being merged has conflicting L7 rules with those already in the provided
 // L4PolicyMap for the specified port-protocol tuple, it returns an error.
 //
-// If any rules contain L7 rules that select Host and we should accept
-// all traffic from host (hostWildcardL7 == true) the L7 rules will be
-// translated into L7 wildcards (ie, traffic will be forwarded to the
-// proxy for endpoints matching those labels, but the proxy will allow
-// all such traffic).
-func mergeIngressPortProto(policyCtx PolicyContext, ctx *SearchContext, endpoints api.EndpointSelectorSlice, hostWildcardL7 bool,
+// If any rules contain L7 rules that select Host or Remote Node and we should
+// accept all traffic from host, the L7 rules will be translated into L7
+// wildcards via 'hostWildcardL7'. That is to say, traffic will be
+// forwarded to the proxy for endpoints matching those labels, but the proxy
+// will allow all such traffic.
+func mergeIngressPortProto(policyCtx PolicyContext, ctx *SearchContext, endpoints api.EndpointSelectorSlice, hostWildcardL7 []string,
 	r api.PortRule, p api.PortProtocol, proto api.L4Proto, ruleLabels labels.LabelArray, resMap L4PolicyMap) (int, error) {
 	// Create a new L4Filter
 	filterToMerge, err := createL4IngressFilter(policyCtx, endpoints, hostWildcardL7, r, p, proto, ruleLabels)
@@ -327,7 +327,13 @@ func mergeIngress(policyCtx PolicyContext, ctx *SearchContext, fromEndpoints api
 	// restrictions on these endpoints into L7 allow-all so that the
 	// traffic is always allowed, but is also always redirected through the
 	// proxy
-	hostWildcardL7 := option.Config.AlwaysAllowLocalhost()
+	hostWildcardL7 := make([]string, 0, 2)
+	if option.Config.AlwaysAllowLocalhost() {
+		hostWildcardL7 = append(hostWildcardL7, labels.IDNameHost)
+		if !option.Config.EnableRemoteNodeIdentity {
+			hostWildcardL7 = append(hostWildcardL7, labels.IDNameRemoteNode)
+		}
+	}
 
 	var (
 		cnt int
diff --git a/plugins/Makefile b/plugins/Makefile
index cddec89e15..8ba50778e8 100644
--- a/plugins/Makefile
+++ b/plugins/Makefile
@@ -1,11 +1,16 @@
+# Copyright 2017-2019 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../Makefile.defs
 
-SUBDIRS = cilium-docker cilium-cni
+SUBDIRS := cilium-docker cilium-cni
+
+.PHONY: all $(SUBDIRS) clean install
 
 all: $(SUBDIRS)
 
 $(SUBDIRS): force
-	@ $(MAKE) $(SUBMAKEOPTS) -C $@ all
+	@$(MAKE) $(SUBMAKEOPTS) -C $@ all
 
 clean:
 	$(QUIET)for i in $(SUBDIRS); do $(MAKE) $(SUBMAKEOPTS) -C $$i clean; done
diff --git a/plugins/cilium-cni/Makefile b/plugins/cilium-cni/Makefile
index 740c463185..c87217e635 100644
--- a/plugins/cilium-cni/Makefile
+++ b/plugins/cilium-cni/Makefile
@@ -1,20 +1,22 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../../Makefile.defs
 
-all: cilium-cni
+TARGET := cilium-cni
+
+.PHONY: all $(TARGET) clean install
 
-TARGET=cilium-cni
+all: $(TARGET)
+
+$(TARGET):
+	@$(ECHO_GO)
+	$(QUIET)$(GO_BUILD) -o $@
 
 clean:
 	@$(ECHO_CLEAN)
-	$(QUIET)$(GO) clean $(GOCLEAN)
 	-$(QUIET)rm -f $(TARGET)
-
-SOURCES := $(shell find ../../api/v1/models ../../common ../../pkg/client ../../pkg/endpoint . -name '*.go')
-
-$(TARGET): $(SOURCES)
-	@$(ECHO_GO)
-	# Compile without cgo to allow use of cilium-cni on non-glibc platforms - see GH-5055
-	$(QUIET)$(GO) build $(GOBUILD) -o $(TARGET)
+	$(QUIET)$(GO_CLEAN)
 
 install:
 	$(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(CNICONFDIR)
diff --git a/plugins/cilium-docker/Makefile b/plugins/cilium-docker/Makefile
index 1da6f95264..5d57847eb5 100644
--- a/plugins/cilium-docker/Makefile
+++ b/plugins/cilium-docker/Makefile
@@ -1,22 +1,27 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../../Makefile.defs
 
-TARGET=cilium-docker
+TARGET := cilium-docker
 
-all: $(TARGET)
+.PHONY: all $(TARGET) clean install
 
-SOURCES := $(shell find ../../api/v1 ../../common ../../pkg/client ../../pkg/endpoint driver . -name '*.go')
+all: $(TARGET)
 
-$(TARGET): $(SOURCES)
+$(TARGET):
 	@$(ECHO_GO)
-	$(QUIET)$(GO) build $(GOBUILD) -o $(TARGET)
-
-run:
-	./cilium-docker -d
+	$(QUIET)$(GO_BUILD) -o $@
 
 clean:
 	@$(ECHO_CLEAN)
-	$(QUIET)$(GO) clean $(GOCLEAN)
-	-$(QUIET) rm -f $(TARGET)
+	-$(QUIET)rm -f $(TARGET)
+	$(QUIET)$(GO_CLEAN)
 
 install:
+	$(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR)
 	$(QUIET)$(INSTALL) -m 0755 $(TARGET) $(DESTDIR)$(BINDIR)
+
+.PHONY: run
+run:
+	./cilium-docker -d
diff --git a/proxylib/Makefile b/proxylib/Makefile
index eee8678f19..154060ee52 100644
--- a/proxylib/Makefile
+++ b/proxylib/Makefile
@@ -1,22 +1,25 @@
-include ../Makefile.quiet
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../Makefile.defs
 
-PROXYLIB_GOLDFLAGS=$(GOLDFLAGS) -extldflags -Wl,-soname,libcilium.so.$(VERSION_MAJOR)
-PROXYLIB_GOBUILD=-ldflags '$(PROXYLIB_GOLDFLAGS)'
+EXTRA_GO_BUILD_LDFLAGS = -extldflags -Wl,-soname,libcilium.so.$(VERSION_MAJOR)
 
-TARGET=libcilium.so
-DEPS := $(shell find ../pkg accesslog npds test . \( -name '*.go' ! -name '*_test.go' \))
-$(TARGET): $(DEPS)
-	@$(ECHO_GO)
-	$(QUIET)$(_GO) build $(GOBUILD) $(PROXYLIB_GOBUILD) -o $@.$(VERSION_MAJOR) -buildmode=c-shared
-	$(QUIET)ln -sf $@.$(VERSION_MAJOR) $@ || cp $@.$(VERSION_MAJOR) $@
+TARGET := libcilium.so
+
+.PHONY: all $(TARGET) clean install
 
 all: $(TARGET)
 
+$(TARGET):
+	@$(ECHO_GO)
+	$(QUIET)$(GO_BUILD_WITH_CGO) -o $@.$(VERSION_MAJOR) -buildmode=c-shared
+	$(QUIET)ln -sf $@.$(VERSION_MAJOR) $@ || cp $@.$(VERSION_MAJOR) $@
+
 clean:
 	@$(ECHO_CLEAN)
 	-$(QUIET)rm -f $(TARGET)
-	$(QUIET)$(_GO) clean $(GOCLEAN)
+	$(QUIET)$(GO_CLEAN)
 
 install:
 	$(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(LIBDIR)
diff --git a/tools/Makefile b/tools/Makefile
index f7e31d3836..4e0e16cc6e 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -1,6 +1,11 @@
+# Copyright 2017-2019 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../Makefile.defs
 
-SUBDIRS = alignchecker maptool
+SUBDIRS := alignchecker maptool
+
+.PHONY: all $(SUBDIRS) clean install
 
 all: $(SUBDIRS)
 
@@ -15,4 +20,3 @@ install:
 
 .PHONY: force
 force :;
-
diff --git a/tools/alignchecker/Makefile b/tools/alignchecker/Makefile
index f5c3013383..6e3a4c7a9c 100644
--- a/tools/alignchecker/Makefile
+++ b/tools/alignchecker/Makefile
@@ -1,17 +1,22 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../../Makefile.defs
 
-TARGET=cilium-align-checker
-SOURCES := $(shell find ../../pkg/bpf . \( -name '*.go' ! -name '*_test.go' \))
-$(TARGET): $(SOURCES)
-	@$(ECHO_GO)
-	$(QUIET)$(GO) build $(GOBUILD) -o $(TARGET)
+TARGET := cilium-align-checker
+
+.PHONY: all $(TARGET) $(SUBDIRS) clean install
 
 all: $(TARGET)
 
+$(TARGET):
+	@$(ECHO_GO)
+	$(QUIET)$(GO_BUILD) -o $@
+
 clean:
 	@$(ECHO_CLEAN)
 	-$(QUIET)rm -f $(TARGET)
-	$(QUIET)$(GO) clean $(GOCLEAN)
+	$(QUIET)$(GO_CLEAN)
 
 install:
 	$(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR)
diff --git a/tools/maptool/Makefile b/tools/maptool/Makefile
index 481c37fc73..ba177f5bdf 100644
--- a/tools/maptool/Makefile
+++ b/tools/maptool/Makefile
@@ -1,17 +1,22 @@
+# Copyright 2017-2020 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../../Makefile.defs
 
-TARGET=maptool
-SOURCES := $(shell find ../../pkg/bpf . \( -name '*.go' ! -name '*_test.go' \))
-$(TARGET): $(SOURCES)
-	@$(ECHO_GO)
-	$(QUIET)$(GO) build $(GOBUILD) -o $(TARGET)
+TARGET := maptool
+
+.PHONY: all $(TARGET) $(SUBDIRS) clean install
 
 all: $(TARGET)
 
+$(TARGET):
+	@$(ECHO_GO)
+	$(QUIET)$(GO_BUILD) -o $@
+
 clean:
 	@$(ECHO_CLEAN)
 	-$(QUIET)rm -f $(TARGET)
-	$(QUIET)$(GO) clean $(GOCLEAN)
+	$(QUIET)$(GO_CLEAN)
 
 install:
 	$(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR)


Test patch:
diff --git a/pkg/policy/api/entity_test.go b/pkg/policy/api/entity_test.go
index 76f16c6e75..2cbfc33f76 100644
--- a/pkg/policy/api/entity_test.go
+++ b/pkg/policy/api/entity_test.go
@@ -36,7 +36,7 @@ func (s EntitySlice) matches(ctx labels.LabelArray) bool {
 }
 
 func (s *PolicyAPITestSuite) TestEntityMatches(c *C) {
-	InitEntities("cluster1")
+	InitEntities("cluster1", false)
 
 	c.Assert(EntityHost.matches(labels.ParseLabelArray("reserved:host")), Equals, true)
 	c.Assert(EntityHost.matches(labels.ParseLabelArray("reserved:host", "id:foo")), Equals, true)
@@ -79,7 +79,7 @@ func (s *PolicyAPITestSuite) TestEntityMatches(c *C) {
 }
 
 func (s *PolicyAPITestSuite) TestEntitySliceMatches(c *C) {
-	InitEntities("cluster1")
+	InitEntities("cluster1", false)
 
 	slice := EntitySlice{EntityHost, EntityWorld}
 	c.Assert(slice.matches(labels.ParseLabelArray("reserved:host")), Equals, true)
@@ -88,3 +88,32 @@ func (s *PolicyAPITestSuite) TestEntitySliceMatches(c *C) {
 	c.Assert(slice.matches(labels.ParseLabelArray("reserved:none")), Equals, false)
 	c.Assert(slice.matches(labels.ParseLabelArray("id=foo")), Equals, false)
 }
+
+func (s *PolicyAPITestSuite) TestEntityHostAllowsRemoteNode(c *C) {
+	tests := []struct {
+		name                  string
+		treatRemoteNodeAsHost bool
+		expectedMatches       labels.LabelArray
+		expectedNonMatches    labels.LabelArray
+	}{
+		{
+			"host entity selects remote-node identity",
+			true,
+			labels.ParseLabelArray("reserved:remote-node"),
+			labels.ParseLabelArray("reserved:all"),
+		},
+		{
+			"host entity does not select remote-node identity",
+			false,
+			labels.ParseLabelArray("reserved:host"),
+			labels.ParseLabelArray("reserved:remote-node"),
+		},
+	}
+
+	for _, tt := range tests {
+		InitEntities("cluster1", tt.treatRemoteNodeAsHost)
+		hostSelector := EntitySelectorMapping[EntityHost]
+		c.Assert(hostSelector.Matches(tt.expectedMatches), Equals, true, Commentf("Test Name: %s", tt.name))
+		c.Assert(hostSelector.Matches(tt.expectedNonMatches), Equals, false, Commentf("Test Name: %s", tt.name))
+	}
+}
diff --git a/pkg/policy/l4_filter_test.go b/pkg/policy/l4_filter_test.go
index 909d31b6c4..532c120f66 100644
--- a/pkg/policy/l4_filter_test.go
+++ b/pkg/policy/l4_filter_test.go
@@ -81,7 +81,19 @@ func (p *testPolicyContextType) GetEnvoyHTTPRules(*api.L7Rules) (*cilium.HttpNet
 	return nil, true
 }
 
-var testPolicyContext *testPolicyContextType
+var (
+	testPolicyContext               *testPolicyContextType
+	cachedRemoteNodeIdentitySetting bool
+)
+
+func (ds *PolicyTestSuite) SetUpSuite(c *C) {
+	cachedRemoteNodeIdentitySetting = option.Config.EnableRemoteNodeIdentity
+	option.Config.EnableRemoteNodeIdentity = true
+}
+
+func (ds *PolicyTestSuite) TearDownSuite(c *C) {
+	option.Config.EnableRemoteNodeIdentity = cachedRemoteNodeIdentitySetting
+}
 
 // Tests in this file:
 //
diff --git a/pkg/policy/l4_test.go b/pkg/policy/l4_test.go
index 2a8ebd5362..9a86a6eed6 100644
--- a/pkg/policy/l4_test.go
+++ b/pkg/policy/l4_test.go
@@ -48,7 +48,7 @@ func (s *PolicyTestSuite) TestCreateL4Filter(c *C) {
 		// Regardless of ingress/egress, we should end up with
 		// a single L7 rule whether the selector is wildcarded
 		// or if it is based on specific labels.
-		filter, err := createL4IngressFilter(testPolicyContext, eps, false, portrule, tuple, tuple.Protocol, nil)
+		filter, err := createL4IngressFilter(testPolicyContext, eps, nil, portrule, tuple, tuple.Protocol, nil)
 		c.Assert(err, IsNil)
 		c.Assert(len(filter.L7RulesPerSelector), Equals, 1)
 		c.Assert(filter.IsEnvoyRedirect(), Equals, true)
@@ -87,7 +87,7 @@ func (s *PolicyTestSuite) TestCreateL4FilterMissingSecret(c *C) {
 		// Regardless of ingress/egress, we should end up with
 		// a single L7 rule whether the selector is wildcarded
 		// or if it is based on specific labels.
-		_, err := createL4IngressFilter(testPolicyContext, eps, false, portrule, tuple, tuple.Protocol, nil)
+		_, err := createL4IngressFilter(testPolicyContext, eps, nil, portrule, tuple, tuple.Protocol, nil)
 		c.Assert(err, Not(IsNil))
 
 		_, err = createL4EgressFilter(testPolicyContext, eps, portrule, tuple, tuple.Protocol, nil, nil)
diff --git a/test/Makefile b/test/Makefile
index d3d9423d9f..aae5ae6e38 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,3 +1,6 @@
+# Copyright 2017-2019 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../Makefile.defs
 
 provision = true
diff --git a/test/bpf/Makefile b/test/bpf/Makefile
index b92ff59d1a..e3f9bf00dd 100644
--- a/test/bpf/Makefile
+++ b/test/bpf/Makefile
@@ -1,3 +1,6 @@
+# Copyright 2017-2019 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../../Makefile.defs
 
 FLAGS := -I../../bpf/ -I../../bpf/include -I. -D__NR_CPUS__=$(shell nproc) -O2
diff --git a/test/helpers/kubectl.go b/test/helpers/kubectl.go
index 6e8e069cef..0214b35632 100644
--- a/test/helpers/kubectl.go
+++ b/test/helpers/kubectl.go
@@ -35,9 +35,9 @@ import (
 	"github.com/cilium/cilium/test/config"
 	ginkgoext "github.com/cilium/cilium/test/ginkgo-ext"
 	"github.com/cilium/cilium/test/helpers/logutils"
-	"github.com/onsi/gomega"
 
 	"github.com/asaskevich/govalidator"
+	"github.com/onsi/gomega"
 	"github.com/sirupsen/logrus"
 	v1 "k8s.io/api/core/v1"
 )
@@ -481,6 +481,28 @@ func (kub *Kubectl) GetPodsNodes(namespace string, filter string) (map[string]st
 	return res.KVOutput(), nil
 }
 
+func (kub *Kubectl) GetPodOnNodeWithOffset(nodeName string, podFilter string, callOffset int) (string, string) {
+	var podName string
+
+	callOffset++
+
+	podsNodes, err := kub.GetPodsNodes(DefaultNamespace, fmt.Sprintf("-l %s", podFilter))
+	gomega.ExpectWithOffset(callOffset, err).Should(gomega.BeNil(), "Cannot retrieve pods nodes with filter %q", podFilter)
+	gomega.Expect(podsNodes).ShouldNot(gomega.BeEmpty(), "No pod found in namespace %s with filter %q", DefaultNamespace, podFilter)
+	for pod, node := range podsNodes {
+		if node == nodeName {
+			podName = pod
+			break
+		}
+	}
+	gomega.ExpectWithOffset(callOffset, podName).ShouldNot(gomega.BeEmpty(), "Cannot retrieve pod on node %s with filter %q", nodeName, podFilter)
+	podsIPs, err := kub.GetPodsIPs(DefaultNamespace, podFilter)
+	gomega.ExpectWithOffset(callOffset, err).Should(gomega.BeNil(), "Cannot retrieve pods IPs with filter %q", podFilter)
+	gomega.Expect(podsIPs).ShouldNot(gomega.BeEmpty(), "No pod IP found in namespace %s with filter %q", DefaultNamespace, podFilter)
+	podIP := podsIPs[podName]
+	return podName, podIP
+}
+
 // GetSvcIP returns the cluster IP for the given service. If the service
 // does not contain a cluster IP, the function keeps retrying until it has or
 // the context timesout.
@@ -2688,6 +2710,16 @@ func (kub *Kubectl) GetCiliumPodOnNode(namespace string, node string) (string, e
 	return res.Output().String(), nil
 }
 
+// GetNodeInfo provides the node name and IP address based on the label
+// (eg helpers.K8s1 or helpers.K8s2)
+func (kub *Kubectl) GetNodeInfo(label string) (nodeName, nodeIP string) {
+	nodeName, err := kub.GetNodeNameByLabel(label)
+	gomega.ExpectWithOffset(1, err).To(gomega.BeNil(), "Cannot get node by label "+label)
+	nodeIP, err = kub.GetNodeIPByLabel(label, false)
+	gomega.ExpectWithOffset(1, err).Should(gomega.BeNil(), "Can not retrieve Node IP for "+label)
+	return nodeName, nodeIP
+}
+
 // GetCiliumPodOnNodeWithLabel returns the name of the Cilium pod that is running on node with cilium.io/ci-node label
 func (kub *Kubectl) GetCiliumPodOnNodeWithLabel(namespace string, label string) (string, error) {
 	node, err := kub.GetNodeNameByLabel(label)
diff --git a/test/k8sT/Policies.go b/test/k8sT/Policies.go
index 271306b979..bb22d5aae6 100644
--- a/test/k8sT/Policies.go
+++ b/test/k8sT/Policies.go
@@ -65,6 +65,7 @@ var _ = Describe("K8sPolicyTest", func() {
 		backgroundCancel     context.CancelFunc = func() { return }
 		backgroundError      error
 		apps                 = []string{helpers.App1, helpers.App2, helpers.App3}
+		daemonCfg            map[string]string
 	)
 
 	BeforeAll(func() {
@@ -92,10 +93,11 @@ var _ = Describe("K8sPolicyTest", func() {
 		knpAllowEgress = helpers.ManifestGet(kubectl.BasePath(), "knp-default-allow-egress.yaml")
 		cnpMatchExpression = helpers.ManifestGet(kubectl.BasePath(), "cnp-matchexpressions.yaml")
 
-		DeployCiliumOptionsAndDNS(kubectl, ciliumFilename, map[string]string{
+		daemonCfg = map[string]string{
 			"global.tls.secretsBackend": "k8s",
 			"global.debug.verbose":      "flow",
-		})
+		}
+		DeployCiliumOptionsAndDNS(kubectl, ciliumFilename, daemonCfg)
 	})
 
 	AfterEach(func() {
@@ -124,6 +126,15 @@ var _ = Describe("K8sPolicyTest", func() {
 		backgroundCancel()
 	})
 
+	// getMatcher returns a helper.CMDSucess() matcher for success or
+	// failure situations.
+	getMatcher := func(val bool) types.GomegaMatcher {
+		if val {
+			return helpers.CMDSuccess()
+		}
+		return Not(helpers.CMDSuccess())
+	}
+
 	Context("Basic Test", func() {
 		var (
 			ciliumPod        string
@@ -139,15 +150,6 @@ var _ = Describe("K8sPolicyTest", func() {
 				"policy %s cannot be applied in %q namespace", file, namespaceForTest)
 		}
 
-		// getMatcher returns a helper.CMDSucess() matcher for success or
-		// failure situations.
-		getMatcher := func(val bool) types.GomegaMatcher {
-			if val {
-				return helpers.CMDSuccess()
-			}
-			return Not(helpers.CMDSuccess())
-		}
-
 		validateConnectivity := func(expectWorldSuccess, expectClusterSuccess bool) {
 			for _, pod := range []string{appPods[helpers.App2], appPods[helpers.App3]} {
 				By("HTTP connectivity to 1.1.1.1")
@@ -961,6 +963,144 @@ var _ = Describe("K8sPolicyTest", func() {
 
 	})
 
+	Context("Multi-node policy test", func() {
+		const (
+			testDS = "zgroup=testDS"
+
+			// This currently matches GetPodOnNodeWithOffset().
+			testNamespace = helpers.DefaultNamespace
+		)
+		var demoYAML string
+
+		BeforeAll(func() {
+			By("Deploying demo daemonset")
+			demoYAML = helpers.ManifestGet(kubectl.BasePath(), "demo_ds.yaml")
+			res := kubectl.ApplyDefault(demoYAML)
+			res.ExpectSuccess("Unable to apply %s", demoYAML)
+
+			err := kubectl.WaitforPods(testNamespace, fmt.Sprintf("-l %s", testDS), helpers.HelperTimeout)
+			Expect(err).Should(BeNil())
+		})
+
+		AfterAll(func() {
+			// Explicitly ignore result of deletion of resources to
+			// avoid incomplete teardown if any step fails.
+			_ = kubectl.Delete(demoYAML)
+			ExpectAllPodsTerminated(kubectl)
+		})
+
+		AfterEach(func() {
+			By("Cleaning up after the test")
+			cmd := fmt.Sprintf("%s delete --all cnp,netpol -n %s", helpers.KubectlCmd, testNamespace)
+			_ = kubectl.Exec(cmd)
+		})
+
+		Context("validates fromEntities policies", func() {
+			const (
+				HostConnectivityAllow       = true
+				RemoteNodeConnectivityDeny  = false
+				RemoteNodeConnectivityAllow = true
+			)
+
+			var (
+				cnpFromEntitiesHost       string
+				cnpFromEntitiesRemoteNode string
+				// TODO: Add fromEntities tests (GH-10979)
+				//cnpFromEntitiesCluster    string
+				//cnpFromEntitiesWorld      string
+				//cnpFromEntitiesAll        string
+
+				k8s1Name, k8s2Name   string
+				k8s1PodIP, k8s2PodIP string
+			)
+
+			BeforeAll(func() {
+				cnpFromEntitiesHost = helpers.ManifestGet(kubectl.BasePath(), "cnp-from-entities-host.yaml")
+				cnpFromEntitiesRemoteNode = helpers.ManifestGet(kubectl.BasePath(), "cnp-from-entities-remote-node.yaml")
+				k8s1Name, _ = kubectl.GetNodeInfo(helpers.K8s1)
+				k8s2Name, _ = kubectl.GetNodeInfo(helpers.K8s2)
+				_, k8s1PodIP = kubectl.GetPodOnNodeWithOffset(k8s1Name, testDS, 0)
+				_, k8s2PodIP = kubectl.GetPodOnNodeWithOffset(k8s2Name, testDS, 0)
+			})
+
+			AfterAll(func() {
+				By("Redeploying Cilium with default configuration")
+				RedeployCilium(kubectl, ciliumFilename, daemonCfg)
+			})
+
+			validateNodeConnectivity := func(expectHostSuccess, expectRemoteNodeSuccess bool) {
+				By("Checking ingress connectivity from k8s1 node to k8s1 pod (host)")
+				res, err := kubectl.ExecInHostNetNS(context.TODO(), k8s1Name,
+					helpers.CurlFail(k8s1PodIP))
+				Expect(err).To(BeNil(), "Cannot run curl in host netns")
+				ExpectWithOffset(1, res).To(getMatcher(expectHostSuccess),
+					"HTTP ingress connectivity to pod %q from local host", k8s1PodIP)
+
+				By("Checking ingress connectivity from k8s1 node to k8s2 pod (remote-node)")
+				res, err = kubectl.ExecInHostNetNS(context.TODO(), k8s1Name,
+					helpers.CurlFail(k8s2PodIP))
+				Expect(err).To(BeNil(), "Cannot run curl in host netns")
+				ExpectWithOffset(1, res).To(getMatcher(expectRemoteNodeSuccess),
+					"HTTP ingress connectivity to pod %q from remote node", k8s2PodIP)
+			}
+
+			importPolicy := func(file, name string) {
+				_, err := kubectl.CiliumPolicyAction(
+					testNamespace, file, helpers.KubectlApply, helpers.HelperTimeout)
+				ExpectWithOffset(1, err).Should(BeNil(),
+					"policy %s cannot be applied in %q namespace", file, testNamespace)
+			}
+
+			Context("with remote-node identity disabled", func() {
+				BeforeAll(func() {
+					By("Reconfiguring Cilium to disable remote-node identity")
+					newCfg := map[string]string{
+						"global.remoteNodeIdentity": "false",
+					}
+					for k, v := range daemonCfg {
+						newCfg[k] = v
+					}
+					RedeployCilium(kubectl, ciliumFilename, newCfg)
+				})
+
+				It("Allows from all hosts with cnp fromEntities host policy", func() {
+					By("Installing fromEntities host policy")
+					importPolicy(cnpFromEntitiesHost, "from-entities-host")
+
+					By("Checking policy correctness")
+					validateNodeConnectivity(HostConnectivityAllow, RemoteNodeConnectivityAllow)
+				})
+			})
+
+			Context("with remote-node identity enabled", func() {
+				BeforeAll(func() {
+					By("Reconfiguring Cilium to enable remote-node identity")
+					newCfg := map[string]string{
+						"global.remoteNodeIdentity": "true",
+					}
+					for k, v := range daemonCfg {
+						newCfg[k] = v
+					}
+					RedeployCilium(kubectl, ciliumFilename, newCfg)
+				})
+
+				It("Validates fromEntities remote-node policy", func() {
+					By("Installing default-deny ingress policy")
+					importPolicy(cnpDenyIngress, "default-deny-ingress")
+
+					By("Checking that remote-node is disallowed by default")
+					validateNodeConnectivity(HostConnectivityAllow, RemoteNodeConnectivityDeny)
+
+					By("Installing fromEntities remote-node policy")
+					importPolicy(cnpFromEntitiesRemoteNode, "from-entities-remote-node")
+
+					By("Checking policy correctness")
+					validateNodeConnectivity(HostConnectivityAllow, RemoteNodeConnectivityAllow)
+				})
+			})
+		})
+	})
+
 	Context("GuestBook Examples", func() {
 		var (
 			deployment                = "guestbook_deployment.yaml"
diff --git a/test/k8sT/Services.go b/test/k8sT/Services.go
index 0dc229cf0e..9801216ad6 100644
--- a/test/k8sT/Services.go
+++ b/test/k8sT/Services.go
@@ -53,18 +53,6 @@ var _ = Describe("K8sServicesTest", func() {
 		ExpectWithOffset(1, err).Should(BeNil(), fmt.Sprintf("Error creating resource %s: %s", path, err))
 	}
 
-	// This is wrapped this way since BeforeAll sets kubectl and we must only
-	// run this after BeforeAll has completed. This happens during the actual
-	// Context/It/By calls.
-	getNodeInfo := func(label string) (nodeName, nodeIP string) {
-		// Nodes are used in testNodePort and testExternalTrafficPolicyLocal below
-		nodeName, err := kubectl.GetNodeNameByLabel(label)
-		Expect(err).To(BeNil(), "Cannot get node by label "+label)
-		nodeIP, err = kubectl.GetNodeIPByLabel(label, false)
-		Expect(err).Should(BeNil(), "Can not retrieve Node IP for "+label)
-		return nodeName, nodeIP
-	}
-
 	BeforeAll(func() {
 		var err error
 
@@ -219,7 +207,7 @@ var _ = Describe("K8sServicesTest", func() {
 			httpSVCURL := fmt.Sprintf("http://%s/", clusterIP)
 			tftpSVCURL := fmt.Sprintf("tftp://%s/hello", clusterIP)
 
-			k8s1Name, _ := getNodeInfo(helpers.K8s1)
+			k8s1Name, _ := kubectl.GetNodeInfo(helpers.K8s1)
 			status, err := kubectl.ExecInHostNetNS(context.TODO(), k8s1Name,
 				helpers.CurlFail(httpSVCURL))
 			Expect(err).To(BeNil(), "Cannot run curl in host netns")
@@ -288,7 +276,7 @@ var _ = Describe("K8sServicesTest", func() {
 			})
 
 			It("Checks service on same node", func() {
-				k8s1Name, _ := getNodeInfo(helpers.K8s1)
+				k8s1Name, _ := kubectl.GetNodeInfo(helpers.K8s1)
 				status, err := kubectl.ExecInHostNetNS(context.TODO(), k8s1Name,
 					helpers.CurlFail(`"http://[%s]/"`, demoClusterIPv6))
 				Expect(err).To(BeNil(), "Cannot run curl in host netns")
@@ -436,7 +424,7 @@ var _ = Describe("K8sServicesTest", func() {
 					if checkSourceIP {
 						cmd += " | grep client_address="
 					}
-					clientNodeName, clientIP := getNodeInfo(helpers.GetNodeWithoutCilium())
+					clientNodeName, clientIP := kubectl.GetNodeInfo(helpers.GetNodeWithoutCilium())
 					res, err := kubectl.ExecInHostNetNS(context.TODO(), clientNodeName, cmd)
 					Expect(err).Should(BeNil(), "Cannot exec in k8s3 host netns")
 					ExpectWithOffset(1, res).Should(helpers.CMDSuccess(),
@@ -453,25 +441,6 @@ var _ = Describe("K8sServicesTest", func() {
 			doRequestsFromThirdHostWithLocalPort(url, count, checkSourceIP, 0)
 		}
 
-		getPodOnNode := func(nodeName string, podFilter string) (string, string) {
-			var podName string
-			podsNodes, err := kubectl.GetPodsNodes(helpers.DefaultNamespace, fmt.Sprintf("-l %s", podFilter))
-			ExpectWithOffset(2, err).Should(BeNil(), "Cannot retrieve pods nodes with filter %q", podFilter)
-			Expect(podsNodes).ShouldNot(BeEmpty(), "No pod found in namespace %s with filter %q", helpers.DefaultNamespace, podFilter)
-			for pod, node := range podsNodes {
-				if node == nodeName {
-					podName = pod
-					break
-				}
-			}
-			ExpectWithOffset(2, podName).ShouldNot(BeEmpty(), "Cannot retrieve pod on node %s with filter %q", nodeName, podFilter)
-			podsIPs, err := kubectl.GetPodsIPs(helpers.DefaultNamespace, podFilter)
-			ExpectWithOffset(2, err).Should(BeNil(), "Cannot retrieve pods IPs with filter %q", podFilter)
-			Expect(podsIPs).ShouldNot(BeEmpty(), "No pod IP found in namespace %s with filter %q", helpers.DefaultNamespace, podFilter)
-			podIP := podsIPs[podName]
-			return podName, podIP
-		}
-
 		// srcPod:      Name of pod sending the datagram
 		// srcIP:       IPv4 of pod sending the datagram
 		// fromNode:    Node hosting the pod sending the datagram
@@ -495,8 +464,8 @@ var _ = Describe("K8sServicesTest", func() {
 				ciliumPod = ciliumPodK8s2
 			}
 
-			_, dstPodIPK8s1 := getPodOnNode(helpers.K8s1, testDS)
-			_, dstPodIPK8s2 := getPodOnNode(helpers.K8s2, testDS)
+			_, dstPodIPK8s1 := kubectl.GetPodOnNodeWithOffset(helpers.K8s1, testDS, 1)
+			_, dstPodIPK8s2 := kubectl.GetPodOnNodeWithOffset(helpers.K8s2, testDS, 1)
 
 			// Get initial number of packets for the flow we test
 			// from conntrack table. The flow is probably not in
@@ -576,8 +545,8 @@ var _ = Describe("K8sServicesTest", func() {
 
 		testNodePort := func(bpfNodePort bool) {
 			var data v1.Service
-			k8s1Name, k8s1IP := getNodeInfo(helpers.K8s1)
-			k8s2Name, k8s2IP := getNodeInfo(helpers.K8s2)
+			k8s1Name, k8s1IP := kubectl.GetNodeInfo(helpers.K8s1)
+			k8s2Name, k8s2IP := kubectl.GetNodeInfo(helpers.K8s2)
 
 			waitPodsDs()
 
@@ -723,7 +692,7 @@ var _ = Describe("K8sServicesTest", func() {
 
 			err := kubectl.Get(helpers.DefaultNamespace, "service test-nodeport").Unmarshal(&data)
 			Expect(err).Should(BeNil(), "Cannot retrieve service")
-			_, k8s1IP := getNodeInfo(helpers.K8s1)
+			_, k8s1IP := kubectl.GetNodeInfo(helpers.K8s1)
 
 			httpURL := getHTTPLink(k8s1IP, data.Spec.Ports[0].NodePort)
 			tftpURL := getTFTPLink(k8s1IP, data.Spec.Ports[1].NodePort)
@@ -757,8 +726,8 @@ var _ = Describe("K8sServicesTest", func() {
 				tftpURL string
 			)
 
-			k8s1Name, k8s1IP := getNodeInfo(helpers.K8s1)
-			k8s2Name, k8s2IP := getNodeInfo(helpers.K8s2)
+			k8s1Name, k8s1IP := kubectl.GetNodeInfo(helpers.K8s1)
+			k8s2Name, k8s2IP := kubectl.GetNodeInfo(helpers.K8s2)
 
 			// Checks requests are not SNATed when externalTrafficPolicy=Local
 			err := kubectl.Get(helpers.DefaultNamespace, "service test-nodeport-local").Unmarshal(&data)
@@ -800,8 +769,8 @@ var _ = Describe("K8sServicesTest", func() {
 				tftpURL string
 			)
 
-			k8s1Name, _ := getNodeInfo(helpers.K8s1)
-			k8s2Name, k8s2IP := getNodeInfo(helpers.K8s2)
+			k8s1Name, _ := kubectl.GetNodeInfo(helpers.K8s1)
+			k8s2Name, k8s2IP := kubectl.GetNodeInfo(helpers.K8s2)
 
 			httpHostPort := int32(8080)
 			tftpHostPort := int32(6969)
@@ -835,8 +804,8 @@ var _ = Describe("K8sServicesTest", func() {
 
 		testHealthCheckNodePort := func() {
 			var data v1.Service
-			k8s1Name, k8s1IP := getNodeInfo(helpers.K8s1)
-			k8s2Name, k8s2IP := getNodeInfo(helpers.K8s2)
+			k8s1Name, k8s1IP := kubectl.GetNodeInfo(helpers.K8s1)
+			k8s2Name, k8s2IP := kubectl.GetNodeInfo(helpers.K8s2)
 
 			// Service with HealthCheckNodePort that only has backends on k8s2
 			err := kubectl.Get(helpers.DefaultNamespace, "service test-lb-local-k8s2").Unmarshal(&data)
@@ -857,15 +826,15 @@ var _ = Describe("K8sServicesTest", func() {
 
 		testIPv4FragmentSupport := func() {
 			var data v1.Service
-			k8s1Name, k8s1IP := getNodeInfo(helpers.K8s1)
-			k8s2Name, k8s2IP := getNodeInfo(helpers.K8s2)
+			k8s1Name, k8s1IP := kubectl.GetNodeInfo(helpers.K8s1)
+			k8s2Name, k8s2IP := kubectl.GetNodeInfo(helpers.K8s2)
 
 			waitPodsDs()
 
 			// Get testDSClient and testDS pods running on k8s1.
 			// This is because we search for new packets in the
 			// conntrack table for node k8s1.
-			clientPod, clientIP := getPodOnNode(helpers.K8s1, testDSClient)
+			clientPod, clientIP := kubectl.GetPodOnNodeWithOffset(helpers.K8s1, testDSClient, 1)
 
 			err := kubectl.Get(helpers.DefaultNamespace, "service test-nodeport").Unmarshal(&data)
 			Expect(err).Should(BeNil(), "Cannot retrieve service")
@@ -1030,7 +999,7 @@ var _ = Describe("K8sServicesTest", func() {
 					var data v1.Service
 					err := kubectl.Get(helpers.DefaultNamespace, "service test-nodeport").Unmarshal(&data)
 					Expect(err).Should(BeNil(), "Cannot retrieve service")
-					_, k8s1IP := getNodeInfo(helpers.K8s1)
+					_, k8s1IP := kubectl.GetNodeInfo(helpers.K8s1)
 					url := getHTTPLink(k8s1IP, data.Spec.Ports[0].NodePort)
 					doRequestsFromThirdHost(url, 10, true)
 
diff --git a/test/k8sT/assertionHelpers.go b/test/k8sT/assertionHelpers.go
index 72b381885c..cca32d319a 100644
--- a/test/k8sT/assertionHelpers.go
+++ b/test/k8sT/assertionHelpers.go
@@ -92,13 +92,27 @@ func DeployCiliumAndDNS(vm *helpers.Kubectl, ciliumFilename string) {
 	DeployCiliumOptionsAndDNS(vm, ciliumFilename, map[string]string{"global.debug.verbose": "flow"})
 }
 
-// DeployCiliumOptionsAndDNS deploys DNS and cilium with options into the kubernetes cluster
-func DeployCiliumOptionsAndDNS(vm *helpers.Kubectl, ciliumFilename string, options map[string]string) {
+func redeployCilium(vm *helpers.Kubectl, ciliumFilename string, options map[string]string) {
 	By("Installing Cilium")
 	err := vm.CiliumInstall(ciliumFilename, options)
 	Expect(err).To(BeNil(), "Cilium cannot be installed")
 
 	ExpectCiliumRunning(vm)
+}
+
+// RedeployCilium reinstantiates the Cilium DS and ensures it is running.
+//
+// This helper is only appropriate for reconfiguring Cilium in the middle of
+// an existing testsuite that calls DeployCiliumAndDNS(...).
+func RedeployCilium(vm *helpers.Kubectl, ciliumFilename string, options map[string]string) {
+	redeployCilium(vm, ciliumFilename, options)
+	ExpectCiliumReady(vm)
+	ExpectCiliumOperatorReady(vm)
+}
+
+// DeployCiliumOptionsAndDNS deploys DNS and cilium with options into the kubernetes cluster
+func DeployCiliumOptionsAndDNS(vm *helpers.Kubectl, ciliumFilename string, options map[string]string) {
+	redeployCilium(vm, ciliumFilename, options)
 
 	By("Installing DNS Deployment")
 	switch helpers.GetCurrentIntegration() {
diff --git a/test/k8sT/manifests/cnp-from-entities-host.yaml b/test/k8sT/manifests/cnp-from-entities-host.yaml
new file mode 100644
index 0000000000..af35587148
--- /dev/null
+++ b/test/k8sT/manifests/cnp-from-entities-host.yaml
@@ -0,0 +1,11 @@
+apiVersion: "cilium.io/v2"
+kind: CiliumNetworkPolicy
+metadata:
+  name: "from-entities-host"
+spec:
+  endpointSelector:
+    matchLabels:
+      {}
+  ingress:
+  - fromEntities:
+    - host
diff --git a/test/k8sT/manifests/cnp-from-entities-remote-node.yaml b/test/k8sT/manifests/cnp-from-entities-remote-node.yaml
new file mode 100644
index 0000000000..8b4f1c7d7f
--- /dev/null
+++ b/test/k8sT/manifests/cnp-from-entities-remote-node.yaml
@@ -0,0 +1,11 @@
+apiVersion: "cilium.io/v2"
+kind: CiliumNetworkPolicy
+metadata:
+  name: "from-entities-remote-node"
+spec:
+  endpointSelector:
+    matchLabels:
+      {}
+  ingress:
+  - fromEntities:
+    - remote-node
diff --git a/test/startup-script/Makefile b/test/startup-script/Makefile
index ac81e6eb1d..54061df1df 100644
--- a/test/startup-script/Makefile
+++ b/test/startup-script/Makefile
@@ -1,3 +1,6 @@
+# Copyright 2017-2019 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 TAG=v1
 IMAGE=docker.io/cilium/startup-script
 
diff --git a/tests/Makefile b/tests/Makefile
index f50e9f49f2..1978f408c8 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -1,5 +1,7 @@
-include ../Makefile.defs
+# Copyright 2017-2019 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
 
+include ../Makefile.defs
 
 runtime-tests:
 	./run-tests
diff --git a/tests/k8s/Makefile b/tests/k8s/Makefile
index 64aff3b5a4..a298501461 100644
--- a/tests/k8s/Makefile
+++ b/tests/k8s/Makefile
@@ -1,3 +1,6 @@
+# Copyright 2017-2019 Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
 include ../../Makefile.defs
 
 k8s-multi-node-tests: