Repository: cilium/cilium
Pull request #11017 (2020-04-16 14:09:09)

build: Remove `sysctl` from Dockerfiles

coveralls: 
[![Coverage Status](https://coveralls.io/builds/30120917/badge)](https://coveralls.io/builds/30120917)

Coverage decreased (-0.003%) to 46.774% when pulling **41ab925a6012ab4215f7d1156acc7c4b647daa62 on pr/errordeveloper/fix-10683** into **e92fd24cf5adbd7ee7acee9d243e3fc82caf3b7d 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/.travis.yml b/.travis.yml
index 9551df300d..dc5bba082e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,7 +6,7 @@ dist: trusty
 sudo: required
 
 go:
-  - 1.14.1
+  - 1.14.2
 
 arch:
   - amd64
diff --git a/Dockerfile b/Dockerfile
index fc8cf93c8b..4f3f64c379 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -13,7 +13,7 @@ FROM quay.io/cilium/cilium-envoy:a3385205ad620550b35d3b0b651e40898386e6e3 as cil
 # versions to be built while allowing the new versions to make changes
 # that are not backwards compatible.
 #
-FROM quay.io/cilium/cilium-builder:2020-04-02 as builder
+FROM quay.io/cilium/cilium-builder:2020-04-16 as builder
 LABEL maintainer="maintainer@cilium.io"
 WORKDIR /go/src/github.com/cilium/cilium
 COPY . ./
@@ -37,7 +37,7 @@ RUN make LOCKDEBUG=$LOCKDEBUG PKG_BUILD=1 V=$V LIBNETWORK_PLUGIN=$LIBNETWORK_PLU
 # built while allowing the new versions to make changes that are not
 # backwards compatible.
 #
-FROM quay.io/cilium/cilium-runtime:2020-04-14
+FROM quay.io/cilium/cilium-runtime:2020-04-16
 LABEL maintainer="maintainer@cilium.io"
 COPY --from=builder /tmp/install /
 COPY --from=cilium-envoy / /
@@ -46,7 +46,7 @@ COPY plugins/cilium-cni/cni-uninstall.sh /cni-uninstall.sh
 COPY contrib/packaging/docker/init-container.sh /init-container.sh
 WORKDIR /home/cilium
 RUN groupadd -f cilium \
-    && echo ". /etc/profile.d/bash_completion.sh" >> /etc/bash.bashrc \
-    && sysctl -w kernel.core_pattern=/tmp/core.%e.%p.%t
+    && echo ". /etc/profile.d/bash_completion.sh" >> /etc/bash.bashrc
+
 ENV INITSYSTEM="SYSTEMD"
 CMD ["/usr/bin/cilium"]
diff --git a/Dockerfile.builder b/Dockerfile.builder
index 5fa2d3988f..d7495248f4 100644
--- a/Dockerfile.builder
+++ b/Dockerfile.builder
@@ -16,7 +16,7 @@ WORKDIR /go/src/github.com/cilium/cilium
 ENV GOROOT /usr/local/go
 ENV GOPATH /go
 ENV PATH "$GOROOT/bin:$GOPATH/bin:$PATH"
-ENV GO_VERSION 1.14.1
+ENV GO_VERSION 1.14.2
 
 #
 # Build dependencies
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/kubernetes/requirements.rst b/Documentation/kubernetes/requirements.rst
index bf302bd133..5101c0abc6 100644
--- a/Documentation/kubernetes/requirements.rst
+++ b/Documentation/kubernetes/requirements.rst
@@ -45,6 +45,16 @@ the `Kubernets CNI network-plugins documentation <https://kubernetes.io/docs/con
 Mounted BPF filesystem
 ======================
 
+.. Note::
+
+        Some distributions mount the bpf filesystem automatically. Check if the
+        bpf filesystem is mounted by running the command.
+
+        .. code:: shell-session
+
+                  mount | grep /sys/fs/bpf
+                  # if present should output, e.g. "none on /sys/fs/bpf type bpf"...
+
 This step is **required for production** environments but optional for testing
 and development. It allows the ``cilium-agent`` to pin BPF resources to a
 persistent filesystem and make them persistent across restarts of the agent.
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/GO_VERSION b/GO_VERSION
index 63e799cf45..a4cc55716f 100644
--- a/GO_VERSION
+++ b/GO_VERSION
@@ -1,1 +1,1 @@
-1.14.1
+1.14.2
diff --git a/Makefile b/Makefile
index 7953d0f705..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/... \
@@ -475,6 +478,8 @@ postcheck: build
 	$(QUIET)$(MAKE) $(SUBMAKEOPTS) -C Documentation update-cmdref check
 	@$(ECHO_CHECK) contrib/scripts/lock-check.sh
 	$(QUIET) contrib/scripts/lock-check.sh
+	@$(ECHO_CHECK) contrib/scripts/rand-check.sh
+	$(QUIET) contrib/scripts/rand-check.sh
 
 minikube:
 	$(QUIET) contrib/scripts/minikube.sh
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 b/cilium-dev.Dockerfile
index 5627af974e..6f77a8fff1 100644
--- a/cilium-dev.Dockerfile
+++ b/cilium-dev.Dockerfile
@@ -28,7 +28,6 @@ RUN for i in proxylib envoy plugins/cilium-cni bpf cilium daemon cilium-health b
             SKIP_DOCS=true DESTDIR= \
             make -C $i install; done
 RUN groupadd -f cilium \
-    && echo ". /etc/profile.d/bash_completion.sh" >> /etc/bash.bashrc \
-    && sysctl -w kernel.core_pattern=/tmp/core.%e.%p.%t
+    && echo ". /etc/profile.d/bash_completion.sh" >> /etc/bash.bashrc
 ENV INITSYSTEM="SYSTEMD"
 CMD ["/usr/bin/cilium"]
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 4bd52ac76d..fa4a4455d0 100644
--- a/cilium-docker-plugin.Dockerfile
+++ b/cilium-docker-plugin.Dockerfile
@@ -1,10 +1,9 @@
-FROM docker.io/library/golang:1.14.1 as builder
+FROM docker.io/library/golang:1.14.2 as builder
 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 ac5ae3acbb..4995b6a4ea 100644
--- a/cilium-operator.Dockerfile
+++ b/cilium-operator.Dockerfile
@@ -1,10 +1,9 @@
-FROM docker.io/library/golang:1.14.1 as builder
+FROM docker.io/library/golang:1.14.2 as builder
 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/docker/Dockerfile.runtime b/contrib/packaging/docker/Dockerfile.runtime
index 05019794dc..a765b79f34 100644
--- a/contrib/packaging/docker/Dockerfile.runtime
+++ b/contrib/packaging/docker/Dockerfile.runtime
@@ -97,7 +97,7 @@ rm -rf /var/lib/apt/lists/*
 #
 # Go-based tools we need at runtime
 #
-FROM docker.io/library/golang:1.14.1 as runtime-gobuild
+FROM docker.io/library/golang:1.14.2 as runtime-gobuild
 WORKDIR /tmp
 RUN go get -d github.com/google/gops && \
 cd /go/src/github.com/google/gops && \
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/contrib/scripts/rand-check.sh b/contrib/scripts/rand-check.sh
new file mode 100755
index 0000000000..177d950601
--- /dev/null
+++ b/contrib/scripts/rand-check.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2020 Authors of Cilium
+
+# Make sure pkg/rand is used instead of math/rand, see
+# https://github.com/cilium/cilium/issues/10988. It's fine to use math/rand in
+# tests though.
+for l in rand\.NewSource; do
+  m=$(find . -name "*.go" \
+	  -not -name "*_test.go" \
+	  -not -path "./contrib/*" \
+	  -not -path "./pkg/rand/*" \
+	  -not -path "./vendor/*" \
+	  -not -path "./test/*" \
+	  -print0 \
+	  | xargs -0 grep --exclude NewSafeSource "$l")
+  if [[ ! -z "$m" ]] ; then
+    echo "Found $l usage. Please use pkg/rand instead for a concurrency-safe implementation:"
+    echo $m
+    exit 1
+  fi
+done
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/daemon/cmd/status.go b/daemon/cmd/status.go
index 0797bd29f0..f7d95614c7 100644
--- a/daemon/cmd/status.go
+++ b/daemon/cmd/status.go
@@ -17,7 +17,6 @@ package cmd
 import (
 	"context"
 	"fmt"
-	"math/rand"
 	"strings"
 	"time"
 
@@ -32,6 +31,7 @@ import (
 	"github.com/cilium/cilium/pkg/lock"
 	"github.com/cilium/cilium/pkg/node"
 	"github.com/cilium/cilium/pkg/option"
+	"github.com/cilium/cilium/pkg/rand"
 	"github.com/cilium/cilium/pkg/status"
 
 	"github.com/go-openapi/runtime/middleware"
@@ -50,7 +50,7 @@ const (
 	k8sMinimumEventHearbeat = time.Minute
 )
 
-var randGen = rand.New(rand.NewSource(time.Now().UnixNano()))
+var randGen = rand.NewSafeRand(time.Now().UnixNano())
 
 type k8sVersion struct {
 	version          string
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/install/kubernetes/cilium/charts/agent/templates/daemonset.yaml b/install/kubernetes/cilium/charts/agent/templates/daemonset.yaml
index 84bb84bbfb..2108541d92 100644
--- a/install/kubernetes/cilium/charts/agent/templates/daemonset.yaml
+++ b/install/kubernetes/cilium/charts/agent/templates/daemonset.yaml
@@ -86,6 +86,10 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: spec.nodeName
+{{- if .Values.global.ci.kubeCacheMutationDetector }}
+        - name: KUBE_CACHE_MUTATION_DETECTOR
+          value: "true"
+{{- end }}
         - name: CILIUM_K8S_NAMESPACE
           valueFrom:
             fieldRef:
diff --git a/install/kubernetes/cilium/charts/operator/templates/deployment.yaml b/install/kubernetes/cilium/charts/operator/templates/deployment.yaml
index 1283aaac68..9905be9fd0 100644
--- a/install/kubernetes/cilium/charts/operator/templates/deployment.yaml
+++ b/install/kubernetes/cilium/charts/operator/templates/deployment.yaml
@@ -40,6 +40,10 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: spec.nodeName
+{{- if .Values.global.ci.kubeCacheMutationDetector }}
+        - name: KUBE_CACHE_MUTATION_DETECTOR
+          value: "true"
+{{- end }}
         - name: CILIUM_K8S_NAMESPACE
           valueFrom:
             fieldRef:
diff --git a/install/kubernetes/cilium/values.yaml b/install/kubernetes/cilium/values.yaml
index aa86c63f61..3aac00632c 100644
--- a/install/kubernetes/cilium/values.yaml
+++ b/install/kubernetes/cilium/values.yaml
@@ -435,3 +435,8 @@ global:
     # See https://github.com/cilium/hubble/blob/master/Documentation/metrics.md for more comprehensive
     # documentation about Hubble's metric collection.
     metrics: []
+
+  # CI specific options: DO NOT USE IN PRODUCTION.
+  ci:
+    # Make Cilium panic if objects received by k8s are modified.
+    kubeCacheMutationDetector: false
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/operator/ccnp_event.go b/operator/ccnp_event.go
index f1dc6e57e2..770712ac62 100644
--- a/operator/ccnp_event.go
+++ b/operator/ccnp_event.go
@@ -81,9 +81,15 @@ func enableCCNPWatcher() error {
 			AddFunc: func(obj interface{}) {
 				metrics.EventTSK8s.SetToCurrentTime()
 				if cnp := k8s.ObjToSlimCNP(obj); cnp != nil {
-					groups.AddDerivativeCNPIfNeeded(cnp.CiliumNetworkPolicy)
+
+					// We need to deepcopy this structure because we are writing
+					// fields.
+					// See https://github.com/cilium/cilium/blob/27fee207f5422c95479422162e9ea0d2f2b6c770/pkg/policy/api/ingress.go#L112-L134
+					cnpCpy := cnp.DeepCopy()
+
+					groups.AddDerivativeCNPIfNeeded(cnpCpy.CiliumNetworkPolicy)
 					if kvstoreEnabled() {
-						ccnpStatusMgr.StartStatusHandler(cnp)
+						ccnpStatusMgr.StartStatusHandler(cnpCpy)
 					}
 				}
 			},
@@ -94,7 +100,14 @@ func enableCCNPWatcher() error {
 						if k8s.EqualV2CNP(oldCNP, newCNP) {
 							return
 						}
-						groups.UpdateDerivativeCNPIfNeeded(newCNP.CiliumNetworkPolicy, oldCNP.CiliumNetworkPolicy)
+
+						// We need to deepcopy this structure because we are writing
+						// fields.
+						// See https://github.com/cilium/cilium/blob/27fee207f5422c95479422162e9ea0d2f2b6c770/pkg/policy/api/ingress.go#L112-L134
+						newCNPCpy := newCNP.DeepCopy()
+						oldCNPCpy := oldCNP.DeepCopy()
+
+						groups.UpdateDerivativeCNPIfNeeded(newCNPCpy.CiliumNetworkPolicy, oldCNPCpy.CiliumNetworkPolicy)
 					}
 				}
 			},
diff --git a/operator/cnp_event.go b/operator/cnp_event.go
index 5222e3101b..9850338d02 100644
--- a/operator/cnp_event.go
+++ b/operator/cnp_event.go
@@ -89,9 +89,15 @@ func enableCNPWatcher() error {
 			AddFunc: func(obj interface{}) {
 				metrics.EventTSK8s.SetToCurrentTime()
 				if cnp := k8s.ObjToSlimCNP(obj); cnp != nil {
-					groups.AddDerivativeCNPIfNeeded(cnp.CiliumNetworkPolicy)
+
+					// We need to deepcopy this structure because we are writing
+					// fields.
+					// See https://github.com/cilium/cilium/blob/27fee207f5422c95479422162e9ea0d2f2b6c770/pkg/policy/api/ingress.go#L112-L134
+					cnpCpy := cnp.DeepCopy()
+
+					groups.AddDerivativeCNPIfNeeded(cnpCpy.CiliumNetworkPolicy)
 					if kvstoreEnabled() {
-						cnpStatusMgr.StartStatusHandler(cnp)
+						cnpStatusMgr.StartStatusHandler(cnpCpy)
 					}
 				}
 			},
@@ -102,7 +108,14 @@ func enableCNPWatcher() error {
 						if k8s.EqualV2CNP(oldCNP, newCNP) {
 							return
 						}
-						groups.UpdateDerivativeCNPIfNeeded(newCNP.CiliumNetworkPolicy, oldCNP.CiliumNetworkPolicy)
+
+						// We need to deepcopy this structure because we are writing
+						// fields.
+						// See https://github.com/cilium/cilium/blob/27fee207f5422c95479422162e9ea0d2f2b6c770/pkg/policy/api/ingress.go#L112-L134
+						newCNPCpy := newCNP.DeepCopy()
+						oldCNPCpy := oldCNP.DeepCopy()
+
+						groups.UpdateDerivativeCNPIfNeeded(newCNPCpy.CiliumNetworkPolicy, oldCNPCpy.CiliumNetworkPolicy)
 					}
 				}
 			},
diff --git a/pkg/azure/api/api.go b/pkg/azure/api/api.go
index 98c3573d5f..c446d76d24 100644
--- a/pkg/azure/api/api.go
+++ b/pkg/azure/api/api.go
@@ -290,7 +290,7 @@ func (c *Client) GetVpcsAndSubnets(ctx context.Context) (ipamTypes.VirtualNetwor
 }
 
 func generateIpConfigName() string {
-	return "Cilium-" + rand.RandomRuneWithLen(8)
+	return rand.RandomStringWithPrefix("Cilium-", 8)
 }
 
 // AssignPrivateIpAddresses assigns the IPs to the interface as specified by
diff --git a/pkg/backoff/backoff.go b/pkg/backoff/backoff.go
index a0cba1e64e..4c59988783 100644
--- a/pkg/backoff/backoff.go
+++ b/pkg/backoff/backoff.go
@@ -18,17 +18,21 @@ import (
 	"context"
 	"fmt"
 	"math"
-	"math/rand"
 	"time"
 
 	"github.com/cilium/cilium/pkg/logging"
 	"github.com/cilium/cilium/pkg/logging/logfields"
+	"github.com/cilium/cilium/pkg/rand"
 	"github.com/cilium/cilium/pkg/uuid"
 
 	"github.com/sirupsen/logrus"
 )
 
-var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "backoff")
+var (
+	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "backoff")
+
+	randGen = rand.NewSafeRand(time.Now().UnixNano())
+)
 
 // NodeManager is the interface required to implement cluster size dependent
 // intervals
@@ -78,7 +82,7 @@ func CalculateDuration(min, max time.Duration, factor float64, jitter bool, fail
 	}
 
 	if jitter {
-		t = rand.Float64()*(t-minFloat) + minFloat
+		t = randGen.Float64()*(t-minFloat) + minFloat
 	}
 
 	return time.Duration(t)
diff --git a/pkg/datapath/connector/add.go b/pkg/datapath/connector/add.go
index cfeb8df39f..9ddc9e7408 100644
--- a/pkg/datapath/connector/add.go
+++ b/pkg/datapath/connector/add.go
@@ -17,12 +17,12 @@ package connector
 import (
 	"crypto/sha256"
 	"fmt"
-	"math/rand"
 	"net"
 
 	"github.com/cilium/cilium/api/v1/models"
 	"github.com/cilium/cilium/pkg/logging"
 	"github.com/cilium/cilium/pkg/logging/logfields"
+	"github.com/cilium/cilium/pkg/rand"
 	"github.com/cilium/cilium/pkg/sysctl"
 
 	"github.com/containernetworking/plugins/pkg/ns"
@@ -53,23 +53,13 @@ func Endpoint2TempIfName(endpointID string) string {
 	return temporaryInterfacePrefix + truncateString(endpointID, 5)
 }
 
-func randIfStr(num int) string {
-	str := make([]rune, num)
-	for i := range str {
-		str[i] = ifChars[rand.Intn(len(ifChars))]
-	}
-	return string(str)
-}
-
 // Endpoint2TempRandIfName returns a random, temporary interface name for the
 // given endpointID. This is similar to Endpoint2TempIfName() but uses a
 // random string instead of endpoint ID.
 func Endpoint2TempRandIfName() string {
-	return temporaryInterfacePrefix + "_" + randIfStr(5)
+	return temporaryInterfacePrefix + "_" + rand.RandomLowercaseStringWithLen(5)
 }
 
-var ifChars = []rune("abcdefghijklmnopqrstuvwxyz")
-
 func truncateString(epID string, maxLen uint) string {
 	if maxLen <= uint(len(epID)) {
 		return epID[:maxLen]
diff --git a/pkg/k8s/config.go b/pkg/k8s/config.go
index c69a44961c..c44b572619 100644
--- a/pkg/k8s/config.go
+++ b/pkg/k8s/config.go
@@ -80,5 +80,6 @@ func IsEnabled() bool {
 	return config.APIServerURL != "" ||
 		config.KubeconfigPath != "" ||
 		(os.Getenv("KUBERNETES_SERVICE_HOST") != "" &&
-			os.Getenv("KUBERNETES_SERVICE_PORT") != "")
+			os.Getenv("KUBERNETES_SERVICE_PORT") != "") ||
+		os.Getenv("K8S_NODE_NAME") != ""
 }
diff --git a/pkg/k8s/informer/informer.go b/pkg/k8s/informer/informer.go
index 3a17dc1fc4..cab4b4370f 100644
--- a/pkg/k8s/informer/informer.go
+++ b/pkg/k8s/informer/informer.go
@@ -15,6 +15,7 @@
 package informer
 
 import (
+	"fmt"
 	"time"
 
 	"k8s.io/apimachinery/pkg/runtime"
@@ -55,6 +56,8 @@ func NewInformerWithStore(
 	// of update/delete deltas.
 	fifo := cache.NewDeltaFIFO(cache.MetaNamespaceKeyFunc, clientState)
 
+	cacheMutationDetector := cache.NewCacheMutationDetector(fmt.Sprintf("%T", objType))
+
 	cfg := &cache.Config{
 		Queue:            fifo,
 		ListerWatcher:    lw,
@@ -68,6 +71,10 @@ func NewInformerWithStore(
 
 				obj := convertFunc(d.Object)
 
+				// In CI we detect if the objects were modified and panic
+				// this is a no-op in production environments.
+				cacheMutationDetector.AddObject(obj)
+
 				switch d.Type {
 				case cache.Sync, cache.Added, cache.Updated:
 					if old, exists, err := clientState.Get(obj); err == nil && exists {
diff --git a/pkg/k8s/watchers/cilium_clusterwide_network_policy.go b/pkg/k8s/watchers/cilium_clusterwide_network_policy.go
index 1cb2c3fb52..5e8699187b 100644
--- a/pkg/k8s/watchers/cilium_clusterwide_network_policy.go
+++ b/pkg/k8s/watchers/cilium_clusterwide_network_policy.go
@@ -56,7 +56,13 @@ func (k *K8sWatcher) ciliumClusterwideNetworkPoliciesInit(ciliumNPClient *k8s.K8
 					if cnp.RequiresDerivative() {
 						return
 					}
-					err := k.addCiliumNetworkPolicyV2(ciliumNPClient, ccnpEventStore, cnp)
+
+					// We need to deepcopy this structure because we are writing
+					// fields.
+					// See https://github.com/cilium/cilium/blob/27fee207f5422c95479422162e9ea0d2f2b6c770/pkg/policy/api/ingress.go#L112-L134
+					cnpCpy := cnp.DeepCopy()
+
+					err := k.addCiliumNetworkPolicyV2(ciliumNPClient, ccnpEventStore, cnpCpy)
 					k.K8sEventProcessed(metricCCNP, metricCreate, err == nil)
 				}
 			},
@@ -75,7 +81,13 @@ func (k *K8sWatcher) ciliumClusterwideNetworkPoliciesInit(ciliumNPClient *k8s.K8
 							return
 						}
 
-						err := k.updateCiliumNetworkPolicyV2(ciliumNPClient, ccnpEventStore, oldCNP, newCNP)
+						// We need to deepcopy this structure because we are writing
+						// fields.
+						// See https://github.com/cilium/cilium/blob/27fee207f5422c95479422162e9ea0d2f2b6c770/pkg/policy/api/ingress.go#L112-L134
+						oldCNPCpy := oldCNP.DeepCopy()
+						newCNPCpy := newCNP.DeepCopy()
+
+						err := k.updateCiliumNetworkPolicyV2(ciliumNPClient, ccnpEventStore, oldCNPCpy, newCNPCpy)
 						k.K8sEventProcessed(metricCCNP, metricUpdate, err == nil)
 					}
 				}
diff --git a/pkg/k8s/watchers/cilium_network_policy.go b/pkg/k8s/watchers/cilium_network_policy.go
index e2303aacbf..880f8ae079 100644
--- a/pkg/k8s/watchers/cilium_network_policy.go
+++ b/pkg/k8s/watchers/cilium_network_policy.go
@@ -122,7 +122,13 @@ func (k *K8sWatcher) ciliumNetworkPoliciesInit(ciliumNPClient *k8s.K8sCiliumClie
 					if cnp.RequiresDerivative() {
 						return
 					}
-					err := k.addCiliumNetworkPolicyV2(ciliumNPClient, cnpEventStore, cnp)
+
+					// We need to deepcopy this structure because we are writing
+					// fields.
+					// See https://github.com/cilium/cilium/blob/27fee207f5422c95479422162e9ea0d2f2b6c770/pkg/policy/api/ingress.go#L112-L134
+					cnpCpy := cnp.DeepCopy()
+
+					err := k.addCiliumNetworkPolicyV2(ciliumNPClient, cnpEventStore, cnpCpy)
 					k.K8sEventProcessed(metricCNP, metricCreate, err == nil)
 				}
 			},
@@ -141,7 +147,13 @@ func (k *K8sWatcher) ciliumNetworkPoliciesInit(ciliumNPClient *k8s.K8sCiliumClie
 							return
 						}
 
-						err := k.updateCiliumNetworkPolicyV2(ciliumNPClient, cnpEventStore, oldCNP, newCNP)
+						// We need to deepcopy this structure because we are writing
+						// fields.
+						// See https://github.com/cilium/cilium/blob/27fee207f5422c95479422162e9ea0d2f2b6c770/pkg/policy/api/ingress.go#L112-L134
+						oldCNPCpy := oldCNP.DeepCopy()
+						newCNPCpy := newCNP.DeepCopy()
+
+						err := k.updateCiliumNetworkPolicyV2(ciliumNPClient, cnpEventStore, oldCNPCpy, newCNPCpy)
 						k.K8sEventProcessed(metricCNP, metricUpdate, err == nil)
 					}
 				}
diff --git a/pkg/kvstore/etcd.go b/pkg/kvstore/etcd.go
index f40372f2a1..88153c3cd4 100644
--- a/pkg/kvstore/etcd.go
+++ b/pkg/kvstore/etcd.go
@@ -21,7 +21,6 @@ import (
 	"errors"
 	"fmt"
 	"io/ioutil"
-	"math/rand"
 	"net/url"
 	"os"
 	"strconv"
@@ -32,6 +31,7 @@ import (
 	"github.com/cilium/cilium/pkg/defaults"
 	"github.com/cilium/cilium/pkg/lock"
 	"github.com/cilium/cilium/pkg/option"
+	"github.com/cilium/cilium/pkg/rand"
 	"github.com/cilium/cilium/pkg/spanstat"
 	"github.com/cilium/cilium/pkg/versioncheck"
 
@@ -65,7 +65,7 @@ var (
 	// exist or it was expired.
 	ErrLockLeaseExpired = errors.New("transaction did not succeed: lock lease expired")
 
-	randGen = rand.New(rand.NewSource(time.Now().UnixNano()))
+	randGen = rand.NewSafeRand(time.Now().UnixNano())
 )
 
 type etcdModule struct {
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/pkg/proxy/proxy.go b/pkg/proxy/proxy.go
index 3f8d873119..47a240a3a0 100644
--- a/pkg/proxy/proxy.go
+++ b/pkg/proxy/proxy.go
@@ -17,7 +17,6 @@ package proxy
 import (
 	"context"
 	"fmt"
-	"math/rand"
 	"time"
 
 	"github.com/cilium/cilium/api/v1/models"
@@ -30,6 +29,7 @@ import (
 	"github.com/cilium/cilium/pkg/node"
 	"github.com/cilium/cilium/pkg/policy"
 	"github.com/cilium/cilium/pkg/proxy/logger"
+	"github.com/cilium/cilium/pkg/rand"
 	"github.com/cilium/cilium/pkg/revert"
 
 	"github.com/sirupsen/logrus"
@@ -144,7 +144,7 @@ var (
 	// allocatedPorts is the map of all allocated proxy ports
 	allocatedPorts = make(map[uint16]struct{})
 
-	portRandomizer = rand.New(rand.NewSource(time.Now().UnixNano()))
+	portRandomizer = rand.NewSafeRand(time.Now().UnixNano())
 
 	// proxyPorts is a slice of all supported proxy ports
 	// The number and order of entries are fixed, and the fields
diff --git a/pkg/rand/rand_name.go b/pkg/rand/rand_name.go
index d24b4e6def..93f677c9e2 100644
--- a/pkg/rand/rand_name.go
+++ b/pkg/rand/rand_name.go
@@ -15,7 +15,6 @@
 package rand
 
 import (
-	"math/rand"
 	"time"
 )
 
@@ -23,26 +22,38 @@ import (
 // https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang
 
 var (
-	randGen = rand.New(rand.NewSource(time.Now().UnixNano()))
+	randGen = NewSafeRand(time.Now().UnixNano())
 
 	letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
 )
 
-// RandomRuneWithPrefix returns a random name string with fixed prefix
-func RandomRuneWithPrefix(prefix string, n int) string {
-	return prefix + RandomRuneWithLen(n)
+// RandomStringWithPrefix returns a random string of length n + len(prefix) with
+// the given prefix, containing upper- and lowercase runes.
+func RandomStringWithPrefix(prefix string, n int) string {
+	return prefix + RandomStringWithLen(n)
 }
 
-// RandomRuneWithLen returns a random name of specified length
-func RandomRuneWithLen(n int) string {
+func randomStringFromSliceWithLen(runes []rune, n int) string {
 	b := make([]rune, n)
 	for i := range b {
-		b[i] = letterRunes[randGen.Intn(len(letterRunes))]
+		b[i] = letterRunes[randGen.Intn(len(runes))]
 	}
 	return string(b)
 }
 
-// RandomRune returns a random name with a predefined length of 12
-func RandomRune() string {
-	return RandomRuneWithLen(12)
+// RandomStringWithLen returns a random string of specified length containing
+// upper- and lowercase runes.
+func RandomStringWithLen(n int) string {
+	return randomStringFromSliceWithLen(letterRunes, n)
+}
+
+// RandomLowercaseStringWithLen returns a random string of specified length
+// containing lowercase runes.
+func RandomLowercaseStringWithLen(n int) string {
+	return randomStringFromSliceWithLen(letterRunes[:26], n)
+}
+
+// RandomString returns a random string with a predefined length of 12.
+func RandomString() string {
+	return RandomStringWithLen(12)
 }
diff --git a/pkg/rand/safe_rand.go b/pkg/rand/safe_rand.go
new file mode 100644
index 0000000000..e1656f45d1
--- /dev/null
+++ b/pkg/rand/safe_rand.go
@@ -0,0 +1,85 @@
+// Copyright 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.
+
+package rand
+
+import (
+	"math/rand"
+
+	"github.com/cilium/cilium/pkg/lock"
+)
+
+// safeRand is a concurrency-safe source of pseudo-random numbers. The Go
+// stdlib's math/rand.Source is not concurrency-safe. The global source in
+// math/rand would be concurrency safe (due to its internal use of
+// lockedSource), but it is prone to inter-package interference with the PRNG
+// state.
+// Also see https://github.com/cilium/cilium/issues/10988
+type safeRand struct {
+	mu lock.Mutex
+	r  *rand.Rand
+}
+
+func NewSafeRand(seed int64) safeRand {
+	return safeRand{r: rand.New(rand.NewSource(seed))}
+}
+
+func (sr *safeRand) Seed(seed int64) {
+	sr.mu.Lock()
+	sr.r.Seed(seed)
+	sr.mu.Unlock()
+}
+
+func (sr *safeRand) Int63() int64 {
+	sr.mu.Lock()
+	v := sr.r.Int63()
+	sr.mu.Unlock()
+	return v
+}
+
+func (sr *safeRand) Uint32() uint32 {
+	sr.mu.Lock()
+	v := sr.r.Uint32()
+	sr.mu.Unlock()
+	return v
+}
+
+func (sr *safeRand) Uint64() uint64 {
+	sr.mu.Lock()
+	v := sr.r.Uint64()
+	sr.mu.Unlock()
+	return v
+}
+
+func (sr *safeRand) Intn(n int) int {
+	sr.mu.Lock()
+	v := sr.r.Intn(n)
+	sr.mu.Unlock()
+	return v
+}
+
+func (sr *safeRand) Float64() float64 {
+	sr.mu.Lock()
+	v := sr.r.Float64()
+	sr.mu.Unlock()
+	return v
+}
+
+func (sr *safeRand) Perm(n int) []int {
+	sr.mu.Lock()
+	v := sr.r.Perm(n)
+	sr.mu.Unlock()
+	return v
+
+}
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/allocator/allocator_test.go b/pkg/allocator/allocator_test.go
index 7dbd8869d1..165ad34062 100644
--- a/pkg/allocator/allocator_test.go
+++ b/pkg/allocator/allocator_test.go
@@ -205,7 +205,7 @@ func (t TestAllocatorKey) PutKeyFromMap(m map[string]string) AllocatorKey {
 }
 
 func randomTestName() string {
-	return rand.RandomRuneWithPrefix(testPrefix, 12)
+	return rand.RandomStringWithPrefix(testPrefix, 12)
 }
 
 func (s *AllocatorSuite) TestSelectID(c *C) {
diff --git a/pkg/clustermesh/services_test.go b/pkg/clustermesh/services_test.go
index 96a6be3f45..f7bc3f2b93 100644
--- a/pkg/clustermesh/services_test.go
+++ b/pkg/clustermesh/services_test.go
@@ -65,7 +65,7 @@ var (
 func (s *ClusterMeshServicesTestSuite) SetUpTest(c *C) {
 	kvstore.SetupDummy("etcd")
 
-	s.randomName = rand.RandomRune()
+	s.randomName = rand.RandomString()
 
 	kvstore.Client().DeletePrefix(context.TODO(), "cilium/state/services/v1/"+s.randomName)
 	s.svcCache = k8s.NewServiceCache(fakeDatapath.NewNodeAddressing())
diff --git a/pkg/kvstore/allocator/allocator_test.go b/pkg/kvstore/allocator/allocator_test.go
index 13829e45aa..c162ff935b 100644
--- a/pkg/kvstore/allocator/allocator_test.go
+++ b/pkg/kvstore/allocator/allocator_test.go
@@ -96,7 +96,7 @@ func (t TestAllocatorKey) PutKeyFromMap(m map[string]string) allocator.Allocator
 }
 
 func randomTestName() string {
-	return rand.RandomRuneWithPrefix(testPrefix, 12)
+	return rand.RandomStringWithPrefix(testPrefix, 12)
 }
 
 func (s *AllocatorSuite) BenchmarkAllocate(c *C) {
diff --git a/pkg/kvstore/store/store_test.go b/pkg/kvstore/store/store_test.go
index 7eca1ac8ed..cea57febe8 100644
--- a/pkg/kvstore/store/store_test.go
+++ b/pkg/kvstore/store/store_test.go
@@ -142,19 +142,19 @@ func (s *StoreSuite) TestStoreCreation(c *C) {
 	c.Assert(store, IsNil)
 
 	// Missing KeyCreator must result in error
-	store, err = JoinSharedStore(Configuration{Prefix: rand.RandomRune()})
+	store, err = JoinSharedStore(Configuration{Prefix: rand.RandomString()})
 	c.Assert(err, ErrorMatches, "KeyCreator must be specified")
 	c.Assert(store, IsNil)
 
 	// Basic creation should result in default values
-	store, err = JoinSharedStore(Configuration{Prefix: rand.RandomRune(), KeyCreator: newTestType})
+	store, err = JoinSharedStore(Configuration{Prefix: rand.RandomString(), KeyCreator: newTestType})
 	c.Assert(err, IsNil)
 	c.Assert(store, Not(IsNil))
 	c.Assert(store.conf.SynchronizationInterval, Equals, option.Config.KVstorePeriodicSync)
 	store.Close(context.TODO())
 
 	// Test with kvstore client specified
-	store, err = JoinSharedStore(Configuration{Prefix: rand.RandomRune(), KeyCreator: newTestType, Backend: kvstore.Client()})
+	store, err = JoinSharedStore(Configuration{Prefix: rand.RandomString(), KeyCreator: newTestType, Backend: kvstore.Client()})
 	c.Assert(err, IsNil)
 	c.Assert(store, Not(IsNil))
 	c.Assert(store.conf.SynchronizationInterval, Equals, option.Config.KVstorePeriodicSync)
@@ -178,7 +178,7 @@ func expect(check func() bool) error {
 
 func (s *StoreSuite) TestStoreOperations(c *C) {
 	// Basic creation should result in default values
-	store, err := JoinSharedStore(Configuration{Prefix: rand.RandomRune(), KeyCreator: newTestType, Observer: &observer{}})
+	store, err := JoinSharedStore(Configuration{Prefix: rand.RandomString(), KeyCreator: newTestType, Observer: &observer{}})
 	c.Assert(err, IsNil)
 	c.Assert(store, Not(IsNil))
 	defer store.Close(context.TODO())
@@ -221,7 +221,7 @@ func (s *StoreSuite) TestStoreOperations(c *C) {
 func (s *StoreSuite) TestStorePeriodicSync(c *C) {
 	// Create a store with a very short periodic sync interval
 	store, err := JoinSharedStore(Configuration{
-		Prefix:                  rand.RandomRune(),
+		Prefix:                  rand.RandomString(),
 		KeyCreator:              newTestType,
 		SynchronizationInterval: 10 * time.Millisecond,
 		Observer:                &observer{},
@@ -250,7 +250,7 @@ func (s *StoreSuite) TestStorePeriodicSync(c *C) {
 
 func (s *StoreSuite) TestStoreLocalKeyProtection(c *C) {
 	store, err := JoinSharedStore(Configuration{
-		Prefix:                  rand.RandomRune(),
+		Prefix:                  rand.RandomString(),
 		KeyCreator:              newTestType,
 		SynchronizationInterval: time.Hour, // ensure that periodic sync does not interfer
 		Observer:                &observer{},
@@ -302,12 +302,12 @@ func setupStoreCollaboration(c *C, storePrefix, keyPrefix string) *SharedStore {
 }
 
 func (s *StoreSuite) TestStoreCollaboration(c *C) {
-	storePrefix := rand.RandomRune()
+	storePrefix := rand.RandomString()
 
-	collab1 := setupStoreCollaboration(c, storePrefix, rand.RandomRune())
+	collab1 := setupStoreCollaboration(c, storePrefix, rand.RandomString())
 	defer collab1.Close(context.TODO())
 
-	collab2 := setupStoreCollaboration(c, storePrefix, rand.RandomRune())
+	collab2 := setupStoreCollaboration(c, storePrefix, rand.RandomString())
 	defer collab2.Close(context.TODO())
 
 	c.Assert(expect(func() bool {
diff --git a/pkg/lock/stoppable_waitgroup_test.go b/pkg/lock/stoppable_waitgroup_test.go
index 1124e0884b..e2382edb95 100644
--- a/pkg/lock/stoppable_waitgroup_test.go
+++ b/pkg/lock/stoppable_waitgroup_test.go
@@ -167,6 +167,8 @@ func (s *SemaphoredMutexSuite) TestWaitChannel(c *C) {
 func (s *SemaphoredMutexSuite) TestParallelism(c *C) {
 	l := NewStoppableWaitGroup()
 
+	// use math/rand instead of pkg/rand to avoid a test import cycle which
+	// go vet would complain about.
 	randGen := rand.New(rand.NewSource(time.Now().UnixNano()))
 	in := make(chan int)
 	stop := make(chan struct{})
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/pkg/rand/rand_name_test.go b/pkg/rand/rand_name_test.go
index 017b989c62..9f331a4ded 100644
--- a/pkg/rand/rand_name_test.go
+++ b/pkg/rand/rand_name_test.go
@@ -31,13 +31,17 @@ type RandSuite struct{}
 
 var _ = Suite(&RandSuite{})
 
-func (s *RandSuite) TestRandomRune(c *C) {
-	c.Assert(len(RandomRune()), Equals, 12)
+func (s *RandSuite) TestRandomString(c *C) {
+	c.Assert(len(RandomString()), Equals, 12)
 
-	c.Assert(len(RandomRuneWithLen(12)), Equals, 12)
-	c.Assert(len(RandomRuneWithLen(0)), Equals, 0)
+	c.Assert(len(RandomStringWithLen(12)), Equals, 12)
+	c.Assert(len(RandomStringWithLen(0)), Equals, 0)
 
-	str := RandomRuneWithPrefix("foo", 12)
-	c.Assert(len(str), Equals, 15)
-	c.Assert(strings.HasPrefix(str, "foo"), Equals, true)
+	s0 := RandomStringWithPrefix("foo", 12)
+	c.Assert(len(s0), Equals, len("foo")+12)
+	c.Assert(strings.HasPrefix(s0, "foo"), Equals, true)
+
+	s1 := RandomLowercaseStringWithLen(15)
+	c.Assert(len(s1), Equals, 15)
+	c.Assert(strings.ToLower(s1), Equals, s1)
 }
diff --git a/pkg/rand/safe_rand_test.go b/pkg/rand/safe_rand_test.go
new file mode 100644
index 0000000000..b03ab133a5
--- /dev/null
+++ b/pkg/rand/safe_rand_test.go
@@ -0,0 +1,36 @@
+// Copyright 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.
+
+// +build !privileged_tests
+
+package rand
+
+import (
+	"testing"
+)
+
+func TestIntn(t *testing.T) {
+	r0 := NewSafeRand(1)
+	r1 := NewSafeRand(1)
+
+	n := 10
+	for i := 0; i < 100; i++ {
+		v0 := r0.Intn(n)
+		v1 := r1.Intn(n)
+
+		if v0 != v1 {
+			t.Errorf("Intn() returned different values at iteration %d: %d != %d", i, v0, v1)
+		}
+	}
+}
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/docker-compose.yml b/test/docker-compose.yml
index 520ed2f801..5c97264740 100644
--- a/test/docker-compose.yml
+++ b/test/docker-compose.yml
@@ -15,7 +15,7 @@ services:
     command: "etcd -name etcd0 -advertise-client-urls http://0.0.0.0:4002 -listen-client-urls http://0.0.0.0:4002 -initial-cluster-token etcd-cluster-1 -initial-cluster-state new"
     privileged: true
   base_image:
-    image: "quay.io/cilium/cilium-builder:2020-04-02"
+    image: "quay.io/cilium/cilium-builder:2020-04-16"
     volumes:
       - "./../:/go/src/github.com/cilium/cilium/"
     privileged: true
diff --git a/test/helpers/kubectl.go b/test/helpers/kubectl.go
index a47ded4826..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"
 )
@@ -85,21 +85,22 @@ var (
 	// below. These overrides represent a desire to set the default for all
 	// tests, instead of test-specific variations.
 	defaultHelmOptions = map[string]string{
-		"global.registry":               "k8s1:5000/cilium",
-		"agent.image":                   "cilium-dev",
-		"preflight.image":               "cilium-dev", // Set again in init to match agent.image!
-		"global.tag":                    "latest",
-		"operator.image":                "operator",
-		"managed-etcd.registry":         "docker.io/cilium",
-		"global.debug.enabled":          "true",
-		"global.k8s.requireIPv4PodCIDR": "true",
-		"global.pprof.enabled":          "true",
-		"global.logSystemLoad":          "true",
-		"global.bpf.preallocateMaps":    "true",
-		"global.etcd.leaseTTL":          "30s",
-		"global.ipv4.enabled":           "true",
-		"global.ipv6.enabled":           "true",
-		"global.psp.enabled":            "true",
+		"global.registry":                     "k8s1:5000/cilium",
+		"agent.image":                         "cilium-dev",
+		"preflight.image":                     "cilium-dev", // Set again in init to match agent.image!
+		"global.tag":                          "latest",
+		"operator.image":                      "operator",
+		"managed-etcd.registry":               "docker.io/cilium",
+		"global.debug.enabled":                "true",
+		"global.k8s.requireIPv4PodCIDR":       "true",
+		"global.pprof.enabled":                "true",
+		"global.logSystemLoad":                "true",
+		"global.bpf.preallocateMaps":          "true",
+		"global.etcd.leaseTTL":                "30s",
+		"global.ipv4.enabled":                 "true",
+		"global.ipv6.enabled":                 "true",
+		"global.psp.enabled":                  "true",
+		"global.ci.kubeCacheMutationDetector": "true",
 	}
 
 	flannelHelmOverrides = map[string]string{
@@ -480,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.
@@ -2687,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/helpers/utils.go b/test/helpers/utils.go
index e429394cc8..eb05b647b8 100644
--- a/test/helpers/utils.go
+++ b/test/helpers/utils.go
@@ -21,7 +21,6 @@ import (
 	"html/template"
 	"io"
 	"io/ioutil"
-	"math/rand"
 	"os"
 	"path/filepath"
 	"regexp"
@@ -29,6 +28,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/cilium/cilium/pkg/rand"
 	"github.com/cilium/cilium/pkg/versioncheck"
 	"github.com/cilium/cilium/test/config"
 	ginkgoext "github.com/cilium/cilium/test/ginkgo-ext"
@@ -40,7 +40,7 @@ import (
 )
 
 // ensure that our random numbers are seeded differently on each run
-var randGen = rand.New(rand.NewSource(time.Now().UnixNano()))
+var randGen = rand.NewSafeRand(time.Now().UnixNano())
 
 // IsRunningOnJenkins detects if the currently running Ginkgo application is
 // most likely running in a Jenkins environment. Returns true if certain
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 fba465f672..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
@@ -481,8 +450,8 @@ var _ = Describe("K8sServicesTest", func() {
 		// dstPort:     Target endpoint port for sending the datagram
 		doFragmentedRequest := func(srcPod string, srcIP string, fromNode string, dstPodPort int, dstIP string, dstPort int32) {
 			var (
-				blockSize  = 512
-				blockCount = 10
+				blockSize  = 5120
+				blockCount = 1
 				srcPort    = 12345
 			)
 			ciliumPodK8s1, err := kubectl.GetCiliumPodOnNode(helpers.CiliumNamespace, helpers.K8s1)
@@ -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/kubernetes-test.sh b/test/kubernetes-test.sh
index 05f1cdce50..8f93870ac9 100755
--- a/test/kubernetes-test.sh
+++ b/test/kubernetes-test.sh
@@ -60,7 +60,7 @@ git clone https://github.com/kubernetes/kubernetes.git -b ${KUBERNETES_VERSION}
 cd kubernetes
 
 # Kubernetes is only compiling with golang 1.13.4 for versions >=1.17
-GO_VERSION="1.14.1"
+GO_VERSION="1.14.2"
 sudo rm -fr /usr/local/go
 curl -LO https://dl.google.com/go/go${GO_VERSION}.linux-amd64.tar.gz
 sudo tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz
diff --git a/test/packet/scripts/install.sh b/test/packet/scripts/install.sh
index 99219ad10e..f5fb2d07ee 100644
--- a/test/packet/scripts/install.sh
+++ b/test/packet/scripts/install.sh
@@ -5,7 +5,7 @@ set -e
 # Ensure no prompts from apt & co.
 export DEBIAN_FRONTEND=noninteractive
 
-GOLANG_VERSION="1.14.1"
+GOLANG_VERSION="1.14.2"
 VAGRANT_VERSION="2.2.4"
 PACKER_VERSION="1.3.5"
 VIRTUALBOX_VERSION="6.0"
diff --git a/test/runtime/monitor.go b/test/runtime/monitor.go
index 38d2ef5384..41671ea07f 100644
--- a/test/runtime/monitor.go
+++ b/test/runtime/monitor.go
@@ -17,9 +17,7 @@ package RuntimeTest
 import (
 	"context"
 	"fmt"
-	"math/rand"
 	"strings"
-	"time"
 
 	. "github.com/cilium/cilium/test/ginkgo-ext"
 	"github.com/cilium/cilium/test/helpers"
@@ -27,9 +25,6 @@ import (
 	. "github.com/onsi/gomega"
 )
 
-// ensure that our random numbers are seeded differently on each run
-var randGen = rand.New(rand.NewSource(time.Now().UnixNano()))
-
 const (
 	// MonitorDropNotification represents the DropNotification configuration
 	// value for the Cilium monitor
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: