Skip to content

Commit 2bff2e4

Browse files
authored
admin: Enforce policy on admin requests (#1250)
Previously, the admin server admitted all requests. Now the admin server will apply policies as discovered from the control plane. The proxy's default policy is used before policy has been synced from the controller.
1 parent aee27f8 commit 2bff2e4

File tree

4 files changed

+61
-24
lines changed

4 files changed

+61
-24
lines changed

linkerd/app/admin/src/stack.rs

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ struct UnexpectedSni(tls::ServerId, Remote<ClientAddr>);
3838

3939
#[derive(Clone, Debug)]
4040
struct Tcp {
41+
policy: inbound::policy::AllowPolicy,
4142
addr: Local<ServerAddr>,
4243
client: Remote<ClientAddr>,
4344
tls: tls::ConditionalServerTls,
@@ -49,6 +50,12 @@ struct Http {
4950
version: http::Version,
5051
}
5152

53+
#[derive(Clone, Debug)]
54+
struct Permitted {
55+
permit: inbound::policy::Permit,
56+
http: Http,
57+
}
58+
5259
#[derive(Clone)]
5360
struct TlsParams {
5461
identity: Option<LocalCrtKey>,
@@ -63,27 +70,36 @@ impl Config {
6370
pub fn build<B, R>(
6471
self,
6572
bind: B,
73+
policy: impl inbound::policy::CheckPolicy,
6674
identity: Option<LocalCrtKey>,
6775
report: R,
6876
metrics: inbound::Metrics,
6977
trace: trace::Handle,
7078
drain: drain::Watch,
7179
shutdown: mpsc::UnboundedSender<()>,
72-
) -> Result<Task, Error>
80+
) -> Result<Task>
7381
where
7482
R: FmtMetrics + Clone + Send + Sync + Unpin + 'static,
7583
B: Bind<ServerConfig>,
7684
B::Addrs: svc::Param<Remote<ClientAddr>> + svc::Param<Local<ServerAddr>>,
7785
{
7886
let (listen_addr, listen) = bind.bind(&self.server)?;
7987

88+
// Get the policy for the admin server.
89+
let policy = policy.check_policy(OrigDstAddr(listen_addr.into()))?;
90+
8091
let (ready, latch) = crate::server::Readiness::new();
8192
let admin = crate::server::Admin::new(report, ready, shutdown, trace);
8293
let admin = svc::stack(move |_| admin.clone())
83-
.push(metrics.proxy.http_endpoint.to_layer::<classify::Response, _, Http>())
94+
.push(metrics.proxy.http_endpoint.to_layer::<classify::Response, _, Permitted>())
95+
.push_map_target(|(permit, http)| Permitted { permit, http })
96+
.push(inbound::policy::NewAuthorizeHttp::layer(metrics.http_authz.clone()))
8497
.push_on_service(
8598
svc::layers()
8699
.push(errors::NewRespond::layer(|error: Error| -> Result<_> {
100+
if error.is::<inbound::policy::DeniedUnauthorized> () {
101+
return Ok(errors::SyntheticHttpResponse::permission_denied(error));
102+
}
87103
tracing::warn!(%error, "Unexpected error");
88104
Ok(errors::SyntheticHttpResponse::unexpected_error())
89105
}))
@@ -135,12 +151,11 @@ impl Config {
135151
.push(detect::NewDetectService::layer(detect::Config::<http::DetectHttp>::from_timeout(DETECT_TIMEOUT)))
136152
.push(transport::metrics::NewServer::layer(metrics.proxy.transport))
137153
.push_map_target(move |(tls, addrs): (tls::ConditionalServerTls, B::Addrs)| {
138-
// TODO(ver): We should enforce policy here; but we need to permit liveness probes
139-
// for destination pods to startup...
140154
Tcp {
141155
tls,
142156
client: addrs.param(),
143157
addr: addrs.param(),
158+
policy: policy.clone(),
144159
}
145160
})
146161
.push(svc::BoxNewService::layer())
@@ -165,8 +180,7 @@ impl Param<transport::labels::Key> for Tcp {
165180
transport::labels::Key::inbound_server(
166181
self.tls.clone(),
167182
self.addr.into(),
168-
// TODO(ver) enforce policies on the proxy's admin port.
169-
metrics::ServerLabel("default:admin".to_string()),
183+
self.policy.server_label(),
170184
)
171185
}
172186
}
@@ -185,22 +199,39 @@ impl Param<OrigDstAddr> for Http {
185199
}
186200
}
187201

202+
impl Param<Remote<ClientAddr>> for Http {
203+
fn param(&self) -> Remote<ClientAddr> {
204+
self.tcp.client
205+
}
206+
}
207+
208+
impl Param<tls::ConditionalServerTls> for Http {
209+
fn param(&self) -> tls::ConditionalServerTls {
210+
self.tcp.tls.clone()
211+
}
212+
}
213+
214+
impl Param<inbound::policy::AllowPolicy> for Http {
215+
fn param(&self) -> inbound::policy::AllowPolicy {
216+
self.tcp.policy.clone()
217+
}
218+
}
219+
188220
impl Param<metrics::ServerLabel> for Http {
189221
fn param(&self) -> metrics::ServerLabel {
190-
metrics::ServerLabel("default:admin".to_string())
222+
self.tcp.policy.server_label()
191223
}
192224
}
193225

194-
impl Param<metrics::EndpointLabels> for Http {
226+
// === impl Permitted ===
227+
228+
impl Param<metrics::EndpointLabels> for Permitted {
195229
fn param(&self) -> metrics::EndpointLabels {
196230
metrics::InboundEndpointLabels {
197-
tls: self.tcp.tls.clone(),
231+
tls: self.http.tcp.tls.clone(),
198232
authority: None,
199-
target_addr: self.tcp.addr.into(),
200-
policy: metrics::AuthzLabels {
201-
server: self.param(),
202-
authz: "default:all-unauthenticated".to_string(),
203-
},
233+
target_addr: self.http.tcp.addr.into(),
234+
policy: self.permit.labels.clone(),
204235
}
205236
.into()
206237
}

linkerd/app/inbound/src/metrics/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub use linkerd_app_core::metrics::*;
1616
/// Holds outbound proxy metrics.
1717
#[derive(Clone, Debug)]
1818
pub struct Metrics {
19-
pub(crate) http_authz: authz::HttpAuthzMetrics,
19+
pub http_authz: authz::HttpAuthzMetrics,
2020
pub http_errors: error::HttpErrorMetrics,
2121

2222
pub(crate) tcp_authz: authz::TcpAuthzMetrics,

linkerd/app/src/env.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,9 @@ pub fn parse_config<S: Strings>(strings: &S) -> Result<super::Config, EnvError>
470470
allow_discovery: gateway_suffixes?.into_iter().flatten().collect(),
471471
};
472472

473+
let admin_listener_addr = admin_listener_addr?
474+
.unwrap_or_else(|| parse_socket_addr(DEFAULT_ADMIN_LISTEN_ADDR).unwrap());
475+
473476
let inbound = {
474477
let addr = ListenAddr(
475478
inbound_listener_addr?
@@ -552,6 +555,9 @@ pub fn parse_config<S: Strings>(strings: &S) -> Result<super::Config, EnvError>
552555
ports.insert(inbound_port);
553556
}
554557

558+
// Ensure that the admin server port is included in policy discovery.
559+
ports.insert(admin_listener_addr.port());
560+
555561
// The workload, which is opaque from the proxy's point-of-view, is sent to the
556562
// policy controller to support policy discovery.
557563
let workload = strings.get(ENV_POLICY_WORKLOAD)?.ok_or_else(|| {
@@ -723,10 +729,7 @@ pub fn parse_config<S: Strings>(strings: &S) -> Result<super::Config, EnvError>
723729
let admin = super::admin::Config {
724730
metrics_retain_idle: metrics_retain_idle?.unwrap_or(DEFAULT_METRICS_RETAIN_IDLE),
725731
server: ServerConfig {
726-
addr: ListenAddr(
727-
admin_listener_addr?
728-
.unwrap_or_else(|| parse_socket_addr(DEFAULT_ADMIN_LISTEN_ADDR).unwrap()),
729-
),
732+
addr: ListenAddr(admin_listener_addr),
730733
keepalive: inbound.proxy.server.keepalive,
731734
h2_settings,
732735
},

linkerd/app/src/lib.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,16 +151,24 @@ impl Config {
151151
let inbound = Inbound::new(inbound, runtime.clone());
152152
let outbound = Outbound::new(outbound, runtime);
153153

154+
let inbound_policies = {
155+
let dns = dns.resolver;
156+
let metrics = metrics.control;
157+
info_span!("policy").in_scope(|| inbound.build_policies(dns, metrics))
158+
};
159+
154160
let admin = {
155161
let identity = identity.local();
156162
let metrics = inbound.metrics();
163+
let policy = inbound_policies.clone();
157164
let report = inbound
158165
.metrics()
159166
.and_then(outbound.metrics())
160167
.and_then(report);
161168
info_span!("admin").in_scope(move || {
162169
admin.build(
163170
bind_admin,
171+
policy,
164172
identity,
165173
report,
166174
metrics,
@@ -195,9 +203,7 @@ impl Config {
195203
let identity = identity.local();
196204
let inbound_addr = inbound_addr;
197205
let profiles = dst.profiles;
198-
let dns = dns.resolver;
199206
let resolve = dst.resolve;
200-
let control_metrics = metrics.control;
201207

202208
Box::pin(async move {
203209
Self::await_identity(identity)
@@ -210,9 +216,6 @@ impl Config {
210216
.instrument(info_span!("outbound")),
211217
);
212218

213-
let inbound_policies =
214-
info_span!("policy").in_scope(|| inbound.build_policies(dns, control_metrics));
215-
216219
tokio::spawn(
217220
inbound
218221
.serve(

0 commit comments

Comments
 (0)