diff --git a/cmd/controller/main.go b/cmd/controller/main.go
index 6df7039df79..2279cda444c 100644
--- a/cmd/controller/main.go
+++ b/cmd/controller/main.go
@@ -60,6 +60,11 @@ func main() {
 	flag.StringVar(&opts.Images.ImageDigestExporterImage, "imagedigest-exporter-image", "", "The container image containing our image digest exporter binary.")
 	flag.StringVar(&opts.Images.WorkingDirInitImage, "workingdirinit-image", "", "The container image containing our working dir init binary.")
 
+	flag.StringVar(&opts.SpireConfig.TrustDomain, "spire-trust-domain", "example.org", "Experimental: The SPIRE Trust domain to use.")
+	flag.StringVar(&opts.SpireConfig.SocketPath, "spire-socket-path", "/spiffe-workload-api/spire-agent.sock", "Experimental: The SPIRE agent socket for SPIFFE workload API.")
+	flag.StringVar(&opts.SpireConfig.ServerAddr, "spire-server-addr", "spire-server.spire.svc.cluster.local:8081", "Experimental: The SPIRE server address for workload/node registration.")
+	flag.StringVar(&opts.SpireConfig.NodeAliasPrefix, "spire-node-alias-prefix", "/tekton-node/", "Experimental: The SPIRE node alias prefix to use.")
+
 	// This parses flags.
 	cfg := injection.ParseAndGetRESTConfigOrDie()
 
diff --git a/pkg/apis/pipeline/options.go b/pkg/apis/pipeline/options.go
index 2e75adca4c1..6c15c86f365 100644
--- a/pkg/apis/pipeline/options.go
+++ b/pkg/apis/pipeline/options.go
@@ -16,8 +16,13 @@ limitations under the License.
 
 package pipeline
 
+import (
+	spireconfig "github.com/tektoncd/pipeline/pkg/spire/config"
+)
+
 // Options holds options passed to the Tekton Pipeline controllers
 // typically via command-line flags.
 type Options struct {
-	Images Images
+	Images      Images
+	SpireConfig spireconfig.SpireConfig
 }
diff --git a/pkg/reconciler/taskrun/controller.go b/pkg/reconciler/taskrun/controller.go
index 7c58c1caab9..67965f8838e 100644
--- a/pkg/reconciler/taskrun/controller.go
+++ b/pkg/reconciler/taskrun/controller.go
@@ -62,6 +62,7 @@ func NewController(opts *pipeline.Options, clock clock.Clock) func(context.Conte
 			KubeClientSet:     kubeclientset,
 			PipelineClientSet: pipelineclientset,
 			Images:            opts.Images,
+			SpireConfig:       opts.SpireConfig,
 			Clock:             clock,
 			taskRunLister:     taskRunInformer.Lister(),
 			resourceLister:    resourceInformer.Lister(),
diff --git a/pkg/reconciler/taskrun/taskrun.go b/pkg/reconciler/taskrun/taskrun.go
index 038f5392d49..416a5a17767 100644
--- a/pkg/reconciler/taskrun/taskrun.go
+++ b/pkg/reconciler/taskrun/taskrun.go
@@ -24,17 +24,9 @@ import (
 	"strings"
 
 	"go.uber.org/zap"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/credentials"
-
-	entryv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/entry/v1"
-	spiffetypes "github.com/spiffe/spire-api-sdk/proto/spire/api/types"
 
 	"github.com/ghodss/yaml"
 	"github.com/hashicorp/go-multierror"
-	"github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig"
-	"github.com/spiffe/go-spiffe/v2/workloadapi"
 	"github.com/tektoncd/pipeline/pkg/apis/config"
 	"github.com/tektoncd/pipeline/pkg/apis/pipeline"
 	"github.com/tektoncd/pipeline/pkg/apis/pipeline/pod"
@@ -55,6 +47,8 @@ import (
 	"github.com/tektoncd/pipeline/pkg/reconciler/events/cloudevent"
 	"github.com/tektoncd/pipeline/pkg/reconciler/taskrun/resources"
 	"github.com/tektoncd/pipeline/pkg/reconciler/volumeclaim"
+	"github.com/tektoncd/pipeline/pkg/spire"
+	spireconfig "github.com/tektoncd/pipeline/pkg/spire/config"
 	"github.com/tektoncd/pipeline/pkg/taskrunmetrics"
 	_ "github.com/tektoncd/pipeline/pkg/taskrunmetrics/fake" // Make sure the taskrunmetrics are setup
 	"github.com/tektoncd/pipeline/pkg/workspace"
@@ -76,6 +70,7 @@ type Reconciler struct {
 	KubeClientSet     kubernetes.Interface
 	PipelineClientSet clientset.Interface
 	Images            pipeline.Images
+	SpireConfig       spireconfig.SpireConfig
 	Clock             clock.Clock
 
 	// listers index properties about resources
@@ -436,8 +431,8 @@ func (c *Reconciler) reconcile(ctx context.Context, tr *v1beta1.TaskRun, rtr *re
 
 	if podconvert.SidecarsReady(pod.Status) {
 		if config.FromContextOrDefaults(ctx).FeatureFlags.EnableSpire {
-			logger.Warnf("LUMJJB registering SPIRE entry: %v/%v", pod.Namespace, pod.Name)
-			spiffeclient, err := NewSpiffeServerApiClient(ctx)
+			logger.Infof("Registering SPIRE entry: %v/%v", pod.Namespace, pod.Name)
+			spiffeclient, err := spire.NewSpiffeServerApiClient(ctx, c.SpireConfig)
 			if err != nil {
 				logger.Errorf("Failed to establish client with SPIRE server: %v", err)
 				return err
@@ -835,145 +830,3 @@ func willOverwritePodSetAffinity(taskRun *v1beta1.TaskRun) bool {
 	}
 	return taskRun.Annotations[workspace.AnnotationAffinityAssistantName] != "" && podTemplate.Affinity != nil
 }
-
-type SpiffeServerApiClient struct {
-	serverConn   *grpc.ClientConn
-	workloadConn *workloadapi.X509Source
-	entryClient  entryv1.EntryClient
-}
-
-func NewSpiffeServerApiClient(ctx context.Context) (*SpiffeServerApiClient, error) {
-	// Create X509Source
-	// TODO(lumjjb) make sock configurable
-	source, err := workloadapi.NewX509Source(ctx, workloadapi.WithClientOptions(workloadapi.WithAddr("unix:///spiffe-workload-api/spire-agent.sock")))
-	if err != nil {
-		return nil, fmt.Errorf("Unable to create X509Source for SPIFFE client: %w", err)
-	}
-
-	// Create connection
-	tlsConfig := tlsconfig.MTLSClientConfig(source, source, tlsconfig.AuthorizeAny())
-	conn, err := grpc.DialContext(ctx, "spire-server.spire.svc.cluster.local:8081", grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
-	if err != nil {
-		source.Close()
-		return nil, fmt.Errorf("Unable to dial SPIRE server: %w", err)
-	}
-
-	return &SpiffeServerApiClient{
-		serverConn:   conn,
-		workloadConn: source,
-		entryClient:  entryv1.NewEntryClient(conn),
-	}, nil
-}
-
-func (sc *SpiffeServerApiClient) CreateNodeEntry(ctx context.Context, nodeName string) error {
-	selectors := []*spiffetypes.Selector{
-		{
-			Type: "k8s_psat",
-			// TODO: set var
-			Value: "agent_ns:spire",
-		},
-		{
-			Type:  "k8s_psat",
-			Value: "agent_node_name:" + nodeName,
-		},
-	}
-
-	// TODO(LUMJJB) take in trust domain
-	entries := []*spiffetypes.Entry{
-		{
-			SpiffeId: &spiffetypes.SPIFFEID{
-				TrustDomain: "example.org",
-				Path:        fmt.Sprintf("/tekton-node/%v", nodeName),
-			},
-			ParentId: &spiffetypes.SPIFFEID{
-				TrustDomain: "example.org",
-				Path:        "/spire/server",
-			},
-			Selectors: selectors,
-		},
-	}
-
-	req := entryv1.BatchCreateEntryRequest{
-		Entries: entries,
-	}
-
-	resp, err := sc.entryClient.BatchCreateEntry(ctx, &req)
-	if err != nil {
-		return err
-	}
-
-	if len(resp.Results) != 1 {
-		return fmt.Errorf("Batch create entry failed, malformed response expected 1 result")
-	}
-
-	res := resp.Results[0]
-	if codes.Code(res.Status.Code) == codes.AlreadyExists ||
-		codes.Code(res.Status.Code) == codes.OK {
-		return nil
-	}
-
-	return fmt.Errorf("Batch create entry failed, code: %v", res.Status.Code)
-
-}
-
-func (sc *SpiffeServerApiClient) CreateWorkloadEntry(ctx context.Context, tr *v1beta1.TaskRun, pod *corev1.Pod) error {
-	// We can potentially add attestation on the container images as well since
-	// the information is available here.
-	selectors := []*spiffetypes.Selector{
-		{
-			Type:  "k8s",
-			Value: "pod-uid:" + string(pod.UID),
-		},
-		{
-			Type:  "k8s",
-			Value: "pod-name:" + pod.Name,
-		},
-	}
-
-	// TODO(LUMJJB) take in trust domain
-	entries := []*spiffetypes.Entry{
-		{
-			SpiffeId: &spiffetypes.SPIFFEID{
-				TrustDomain: "example.org",
-				Path:        fmt.Sprintf("/ns/%v/taskrun/%v", tr.Namespace, tr.Name),
-			},
-			ParentId: &spiffetypes.SPIFFEID{
-				TrustDomain: "example.org",
-				Path:        fmt.Sprintf("/tekton-node/%v", pod.Spec.NodeName),
-			},
-			Selectors: selectors,
-		},
-	}
-
-	req := entryv1.BatchCreateEntryRequest{
-		Entries: entries,
-	}
-
-	resp, err := sc.entryClient.BatchCreateEntry(ctx, &req)
-	if err != nil {
-		return err
-	}
-
-	if len(resp.Results) != 1 {
-		return fmt.Errorf("Batch create entry failed, malformed response expected 1 result")
-	}
-
-	res := resp.Results[0]
-	if codes.Code(res.Status.Code) == codes.AlreadyExists ||
-		codes.Code(res.Status.Code) == codes.OK {
-		return nil
-	}
-
-	return fmt.Errorf("Batch create entry failed, code: %v", res.Status.Code)
-}
-
-func (sc *SpiffeServerApiClient) Close() {
-	err := sc.serverConn.Close()
-	if err != nil {
-		// Log error
-	}
-	err = sc.workloadConn.Close()
-	if err != nil {
-		// Log error
-	}
-}
diff --git a/pkg/spire/config/config.go b/pkg/spire/config/config.go
new file mode 100644
index 00000000000..58e5b1bcced
--- /dev/null
+++ b/pkg/spire/config/config.go
@@ -0,0 +1,64 @@
+/*
+Copyright 2019 The Tekton Authors
+
+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 config
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+)
+
+// SpireConfig holds the images reference for a number of container images used
+// across tektoncd pipelines.
+// Example:
+//   "trustDomain": "example.org",
+// 	 "socketPath": "/spiffe-workload-api/spire-agent.sock",
+// 	 "serverAddr": "spire-server.spire.svc.cluster.local:8081",
+// 	 "nodeAliasPrefix": "/tekton-node/"
+type SpireConfig struct {
+	TrustDomain     string
+	SocketPath      string
+	ServerAddr      string
+	NodeAliasPrefix string
+}
+
+// Validate returns an error if any image is not set.
+func (c SpireConfig) Validate() error {
+	var unset []string
+	for _, f := range []struct {
+		v, name string
+	}{
+		{c.TrustDomain, "spire-trust-domain"},
+		{c.SocketPath, "spire-socket-path"},
+		{c.ServerAddr, "spire-server-addr"},
+		{c.NodeAliasPrefix, "spire-node-alias-prefix"},
+	} {
+		if f.v == "" {
+			unset = append(unset, f.name)
+		}
+	}
+	if len(unset) > 0 {
+		sort.Strings(unset)
+		return fmt.Errorf("found unset image flags: %s", unset)
+	}
+
+	if !strings.HasPrefix(c.NodeAliasPrefix, "/") {
+		return fmt.Errorf("Spire node alias should start with a /")
+	}
+
+	return nil
+}
diff --git a/pkg/spire/spire.go b/pkg/spire/spire.go
new file mode 100644
index 00000000000..5c1c932fe30
--- /dev/null
+++ b/pkg/spire/spire.go
@@ -0,0 +1,174 @@
+/*
+Copyright 2019 The Tekton Authors
+
+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 spire
+
+import (
+	"context"
+	"fmt"
+
+	entryv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/entry/v1"
+	spiffetypes "github.com/spiffe/spire-api-sdk/proto/spire/api/types"
+
+	corev1 "k8s.io/api/core/v1"
+
+	"github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig"
+	"github.com/spiffe/go-spiffe/v2/workloadapi"
+	"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
+	spireconfig "github.com/tektoncd/pipeline/pkg/spire/config"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/credentials"
+)
+
+type SpiffeServerApiClient struct {
+	serverConn   *grpc.ClientConn
+	workloadConn *workloadapi.X509Source
+	entryClient  entryv1.EntryClient
+	config       spireconfig.SpireConfig
+}
+
+func NewSpiffeServerApiClient(ctx context.Context, c spireconfig.SpireConfig) (*SpiffeServerApiClient, error) {
+	// Create X509Source
+	source, err := workloadapi.NewX509Source(ctx, workloadapi.WithClientOptions(workloadapi.WithAddr("unix://"+c.SocketPath)))
+	if err != nil {
+		return nil, fmt.Errorf("Unable to create X509Source for SPIFFE client: %w", err)
+	}
+
+	// Create connection
+	tlsConfig := tlsconfig.MTLSClientConfig(source, source, tlsconfig.AuthorizeAny())
+	conn, err := grpc.DialContext(ctx, c.ServerAddr, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
+	if err != nil {
+		source.Close()
+		return nil, fmt.Errorf("Unable to dial SPIRE server: %w", err)
+	}
+
+	return &SpiffeServerApiClient{
+		serverConn:   conn,
+		workloadConn: source,
+		entryClient:  entryv1.NewEntryClient(conn),
+		config:       c,
+	}, nil
+}
+
+func (sc *SpiffeServerApiClient) CreateNodeEntry(ctx context.Context, nodeName string) error {
+	selectors := []*spiffetypes.Selector{
+		{
+			Type:  "k8s_psat",
+			Value: "agent_ns:spire",
+		},
+		{
+			Type:  "k8s_psat",
+			Value: "agent_node_name:" + nodeName,
+		},
+	}
+
+	entries := []*spiffetypes.Entry{
+		{
+			SpiffeId: &spiffetypes.SPIFFEID{
+				TrustDomain: sc.config.TrustDomain,
+				Path:        fmt.Sprintf("%v%v", sc.config.NodeAliasPrefix, nodeName),
+			},
+			ParentId: &spiffetypes.SPIFFEID{
+				TrustDomain: sc.config.TrustDomain,
+				Path:        "/spire/server",
+			},
+			Selectors: selectors,
+		},
+	}
+
+	req := entryv1.BatchCreateEntryRequest{
+		Entries: entries,
+	}
+
+	resp, err := sc.entryClient.BatchCreateEntry(ctx, &req)
+	if err != nil {
+		return err
+	}
+
+	if len(resp.Results) != 1 {
+		return fmt.Errorf("Batch create entry failed, malformed response expected 1 result")
+	}
+
+	res := resp.Results[0]
+	if codes.Code(res.Status.Code) == codes.AlreadyExists ||
+		codes.Code(res.Status.Code) == codes.OK {
+		return nil
+	}
+
+	return fmt.Errorf("Batch create entry failed, code: %v", res.Status.Code)
+}
+
+func (sc *SpiffeServerApiClient) CreateWorkloadEntry(ctx context.Context, tr *v1beta1.TaskRun, pod *corev1.Pod) error {
+	// Note: We can potentially add attestation on the container images as well since
+	// the information is available here.
+	selectors := []*spiffetypes.Selector{
+		{
+			Type:  "k8s",
+			Value: "pod-uid:" + string(pod.UID),
+		},
+		{
+			Type:  "k8s",
+			Value: "pod-name:" + pod.Name,
+		},
+	}
+
+	entries := []*spiffetypes.Entry{
+		{
+			SpiffeId: &spiffetypes.SPIFFEID{
+				TrustDomain: sc.config.TrustDomain,
+				Path:        fmt.Sprintf("/ns/%v/taskrun/%v", tr.Namespace, tr.Name),
+			},
+			ParentId: &spiffetypes.SPIFFEID{
+				TrustDomain: sc.config.TrustDomain,
+				Path:        fmt.Sprintf("%v%v", sc.config.NodeAliasPrefix, pod.Spec.NodeName),
+			},
+			Selectors: selectors,
+		},
+	}
+
+	req := entryv1.BatchCreateEntryRequest{
+		Entries: entries,
+	}
+
+	resp, err := sc.entryClient.BatchCreateEntry(ctx, &req)
+	if err != nil {
+		return err
+	}
+
+	if len(resp.Results) != 1 {
+		return fmt.Errorf("Batch create entry failed, malformed response expected 1 result")
+	}
+
+	res := resp.Results[0]
+	if codes.Code(res.Status.Code) == codes.AlreadyExists ||
+		codes.Code(res.Status.Code) == codes.OK {
+		return nil
+	}
+
+	return fmt.Errorf("Batch create entry failed, code: %v", res.Status.Code)
+}
+
+func (sc *SpiffeServerApiClient) Close() {
+	err := sc.serverConn.Close()
+	if err != nil {
+		// Log error
+	}
+	err = sc.workloadConn.Close()
+	if err != nil {
+		// Log error
+	}
+}