Expand description
Client bindings for the Juniper JET gRPC API.
Building
The gRPC service bindings are generated by tonic-build at build-time.
The protoc
command line tool (included in the protobuf
package on most Linux distributions)
must be available at compile time.
Features
The JET gRPC service definitions vary be Junos version.
Support for each major.minor
Junos version is enabled by a corresponding
junos-{major}-{minor}
cargo feature.
Only the latest supported Junos version is enabled by default, via the latest
cargo feature.
Multiple versions can be enabled simultaneously, but will impact compile times.
Examples
To run the examples, a device running the appropriate version of Junos should be available, and configured to enable the JET gRPC server endpoint.
For convenience a set of RSA keys and associated X.509 certificates are
provided in examples/pki/
. These are for testing purposes only and should
never be used for production deployments.
Router configuration
Minimally, the device should be configured with the appropriate server TLS certificate.
First, copy the PEM encoded server certificate and key bundle onto the device:
$ scp examples/pki/server.bundle.pem $JUNOS_DEVICE_ADDR:/var/tmp/
And load the file contents into the configuration:
[edit]
support@router# set security certificates local jet-example load-key-file /var/tmp/server.bundle.pem
The server X.509 certificate contains a Subject Alternative Name extension
containing router.example.net
.
Then configure the JET service to use the certificate:
[edit system services extension-service]
support@router# show
request-response {
grpc {
ssl {
local-certificate jet-example;
mutual-authentication {
certificate-authority ca;
client-certificate-request request-certificate-and-verify;
}
}
max-connections 8;
routing-instance mgmt_junos;
}
}
To avoid having verification fail without hacking at your local DNS
infrastructure, the example application provides a CLI option
--server-tls-domain-name
that will override the name used during server
certificate verification.
Mutual TLS authentication
Optionally, to enable mutual TLS authentication, the Junos device should be configured to verify the presented client certificate using the appropriate CA certificate.
Transfer the CA X.509 certificate to the device:
$ scp examples/pki/ca.crt $JUNOS_DEVICE_ADDR:/var/tmp/
Configure a ca-profile
for the CA:
[edit security pki]
support@router# show
ca-profile ca {
ca-identity ca;
}
And load the file contents into configured ca-profile
:
support@router> request security pki ca-certificate load ca-profile ca filename /var/tmp/ca.crt
Then configure the JET service to require client certificates and to verify using the correct CA:
[edit system services extension-service request-response grpc ssl]
support@router# show
local-certificate jet-example;
mutual-authentication {
certificate-authority ca;
client-certificate-request require-certificate-and-verify;
}
The example application can then be run using --client-cert-path examples/pki/client.crt
and --client-key-path examples/pki/client.key
options.
use std::error::Error;
use std::fs;
use std::path::PathBuf;
use clap::Parser;
use jet::junos_20_4::jnx::jet::{
authentication::{authentication_client::AuthenticationClient, LoginRequest},
common::StatusCode,
management::{
management_client::ManagementClient, op_command_get_request::Command, OpCommandGetRequest,
OpCommandOutputFormat,
},
};
use rpassword::prompt_password;
use simple_logger::SimpleLogger;
use tonic::transport::{Certificate, Channel, ClientTlsConfig, Identity};
/// JET demo application.
#[derive(Debug, Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// Junos operational mode command to execute
command: String,
/// JET server hostname.
#[arg(short = 'H', long)]
host: String,
/// JET service TCP port.
#[arg(short = 'P', long, default_value_t = 32767)]
port: u16,
/// Authentication username.
#[arg(short = 'u', long, default_value = "support")]
username: String,
/// Client certificate path.
#[arg(long, requires("client_key_path"))]
client_cert_path: Option<PathBuf>,
/// Client key path.
#[arg(long, requires("client_cert_path"))]
client_key_path: Option<PathBuf>,
/// CA certificate path.
#[arg(long, default_value = "./examples/pki/ca.crt")]
ca_cert_path: PathBuf,
/// Application client ID.
#[arg(long, default_value = "jet-demo-rs")]
client_id: String,
/// Override the domain name against which the server's TLS certificate is verified.
#[arg(long)]
server_tls_domain_name: Option<String>,
#[command(flatten)]
verbosity: clap_verbosity_flag::Verbosity,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let args = Cli::parse();
SimpleLogger::new()
.with_level(args.verbosity.log_level_filter())
.init()?;
log::debug!("logger initialized");
log::debug!("setting up TLS config");
let tls_config = {
let mut config = ClientTlsConfig::new().ca_certificate(Certificate::from_pem(
fs::read_to_string(args.ca_cert_path)?,
));
if let Some(domain_name) = args.server_tls_domain_name {
log::info!("overriding server TLS domain name: {domain_name}");
config = config.domain_name(domain_name);
}
match (args.client_cert_path, args.client_key_path) {
(Some(cert_path), Some(key_path)) => {
log::info!("enabling mutual TLS authentication");
config.identity(Identity::from_pem(
fs::read_to_string(cert_path)?,
fs::read_to_string(key_path)?,
))
}
(None, None) => config,
_ => unreachable!("cli argument parsing rules should prevent this being reached"),
}
};
log::info!("connecting gRPC channel");
let mut channel = Channel::from_shared(format!("https://{}:{}", args.host, args.port))?
.tls_config(tls_config)?
.connect()
.await?;
log::info!("attempting to authenticate to JET server");
let login_resp = AuthenticationClient::new(&mut channel)
.login(LoginRequest {
client_id: args.client_id,
username: args.username,
password: prompt_password("Password: ")?,
})
.await?
.into_inner();
if let Some(status) = login_resp.status {
match status.code() {
StatusCode::Success => log::info!("authentication successful"),
StatusCode::Failure => {
return Err(format!("authentication failed: {}", status.message).into());
}
};
} else {
return Err(format!("no status in login response message: {:?}", login_resp).into());
};
let mut op_command_resp_stream = ManagementClient::new(&mut channel)
.op_command_get(OpCommandGetRequest {
out_format: OpCommandOutputFormat::OpCommandOutputCli.into(),
command: Some(Command::CliCommand(args.command)),
})
.await?
.into_inner();
while let Some(op_command_resp) = op_command_resp_stream.message().await? {
if let Some(status) = op_command_resp.status {
match status.code() {
StatusCode::Success => println!("{}", op_command_resp.data),
StatusCode::Failure => {
return Err(format!("op command failed: {}", status.message).into());
}
}
} else {
return Err(format!(
"no status in op_command response message: {:?}",
op_command_resp
)
.into());
}
}
Ok(())
}
Modules
- junos_19_1
junos-19-1
Generated JET gRPC service definitions for Junos19.1
. - junos_19_2
junos-19-2
Generated JET gRPC service definitions for Junos19.2
. - junos_19_3
junos-19-3
Generated JET gRPC service definitions for Junos19.3
. - junos_19_4
junos-19-4
Generated JET gRPC service definitions for Junos19.4
. - junos_20_1
junos-20-1
Generated JET gRPC service definitions for Junos20.1
. - junos_20_2
junos-20-2
Generated JET gRPC service definitions for Junos20.2
. - junos_20_3
junos-20-3
Generated JET gRPC service definitions for Junos20.3
. - junos_20_4
junos-20-4
Generated JET gRPC service definitions for Junos20.4
. - junos_21_1
junos-21-1
Generated JET gRPC service definitions for Junos21.1
. - junos_21_2
junos-21-2
Generated JET gRPC service definitions for Junos21.2
. - junos_21_3
junos-21-3
Generated JET gRPC service definitions for Junos21.3
. - junos_21_4
junos-21-4
Generated JET gRPC service definitions for Junos21.4
. - junos_22_2
junos-22-2
Generated JET gRPC service definitions for Junos22.2
. - junos_22_3
junos-22-3
Generated JET gRPC service definitions for Junos22.3
. - junos_22_4
junos-22-4
Generated JET gRPC service definitions for Junos22.4
. - junos_23_1
junos-23-1
Generated JET gRPC service definitions for Junos23.1
. - latest
latest
Generated JET gRPC service definitions for the latest Junos version.