I'm using rumqttc tls with rustls
Error = Tls(Io(Custom { kind: InvalidData, error: InvalidCertificateData("invalid peer certificate: CertNotValidForName") }))
this is my function:
#[cfg(feature = "use-rustls")]
#[tokio::main]
pub async fn test_mqtt() -> Result<(), Box<dyn Error>> {
use rumqttc::{self, AsyncClient, Key, MqttOptions, TlsConfiguration, Transport};
let mut mqttoptions = MqttOptions::new("test", "test.test.com", 1000);
mqttoptions.set_credentials("test", "test");
mqttoptions.set_keep_alive(std::time::Duration::from_secs(5));
let ca = include_bytes!("../certs/ca.crt");
let client_cert = include_bytes!("../certs/client.crt");
let client_key = include_bytes!("../certs/client.key");
let transport = Transport::Tls(TlsConfiguration::Simple {
ca: ca.to_vec(),
alpn: None,
client_auth: Some((client_cert.to_vec(), Key::RSA(client_key.to_vec()))),
});
mqttoptions.set_transport(transport);
let (client, mut eventloop) = AsyncClient::new(mqttoptions, 10);
client.subscribe("test/test/test", QoS::AtMostOnce).await.unwrap();
client.subscribe("test/test/test/logs", QoS::AtMostOnce).await.unwrap();
thread::spawn(move || {
client.publish("test/test/test", QoS::AtLeastOnce, false, "test");
client.publish("test/test/test/logs", QoS::AtLeastOnce, false, "test");
thread::sleep(Duration::from_millis(100));
});
loop {
match eventloop.poll().await {
Ok(v) => {
println!("Event = {:?}", v);
}
Err(e) => {
println!("Error = {:?}", e);
break;
}
}
}
Ok(())
}
This results in
2022-11-18 16:11:46 - WARN: Sending fatal alert BadCertificate
Error = Tls(Io(Custom { kind: InvalidData, error: InvalidCertificateData("invalid peer certificate: CertNotValidForName") }))
The rustls certificate verification through webpki is quite strict. You may circumvent it by implementing a custom ServerCertVerifier.
Take a look at: https://users.rust-lang.org/t/rustls-connecting-without-certificate-in-local-network/83822/4
Related
My 20th day learning Rust and I try to code a reverse proxy that can authenticate.
Got error in line 30: error[E0282]: type annotations needed for &Target. I tried to follow the help by compiler "consider giving this closure parameter an explicit type where the type for type parameter Target is specified"
But i got even more errors and confuse.
Please help.
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};
use std::convert::Infallible;
use std::net::{IpAddr, SocketAddr};
async fn authenticate(req: Request<Body>) -> Result<Request<Body>, Infallible> {
// Perform authentication checks here.
// For example, you can check for a valid API key in the request header.
Ok(req)
}
async fn handle(client_ip: IpAddr, req: Request<Body>) -> Result<Response<Body>, Infallible> {
// Forward the request to another server.
let target_url = "http://127.0.0.1:13901";
match hyper_reverse_proxy::call(client_ip, target_url, req).await {
Ok(response) => Ok(response),
Err(error) => {
// println!("Error forwarding request: {}", error);
Ok(Response::builder()
.status(hyper::StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::empty())
.unwrap())
}
}
}
#[tokio::main]
async fn main() {
let make_service = make_service_fn(|conn| async { // <<-- Error!
let client_ip = conn.remote_addr().ip();
Ok::<_, Infallible>(service_fn(move |req| {
let client_ip = client_ip.clone();
async {
match authenticate(req).await {
Ok(req) => handle(client_ip, req).await,
Err(_) => {
eprintln!("Authentication failed");
Ok(Response::builder()
.status(hyper::StatusCode::UNAUTHORIZED)
.body(Body::empty())
.unwrap())
}
}
}
}))
});
let bind_addr = "127.0.0.1:8000";
let addr: SocketAddr = bind_addr.parse().expect("Could not parse ip:port.");
let server = Server::bind(&addr).serve(make_service);
println!("Running reverse proxy on {:?}", addr);
if let Err(error) = server.await {
eprintln!("Error running server: {}", error);
}
}
my cargo.toml
[dependencies]
hyper-reverse-proxy = "0.5"
hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1", features = ["full"] }
the signature of the functions is
pub fn make_service_fn<F, Target, Ret>(f: F) -> MakeServiceFn<F>
where
F: FnMut(&Target) -> Ret,
Ret: Future,
The target is a generic parameter and according to example from documentation
use std::convert::Infallible;
use hyper::{Body, Request, Response, Server};
use hyper::server::conn::AddrStream;
use hyper::service::{make_service_fn, service_fn};
let addr = ([127, 0, 0, 1], 3000).into();
let make_svc = make_service_fn(|socket: &AddrStream| {
let remote_addr = socket.remote_addr();
async move {
Ok::<_, Infallible>(service_fn(move |_: Request<Body>| async move {
Ok::<_, Infallible>(
Response::new(Body::from(format!("Hello, {}!", remote_addr)))
)
}))
}
});
// Then bind and serve...
let server = Server::bind(&addr)
.serve(make_svc);
// Finally, spawn `server` onto an Executor...
if let Err(e) = server.await {
eprintln!("server error: {}", e);
}
we can pass reference to AddrStream which you have to specify since rust cannot infer this...
so the fixed code looks like this
let make_service = make_service_fn(|conn: &AddrStream| {
let client_ip = conn.remote_addr().ip();
async move {
Ok::<_, Infallible>(service_fn(move |req| {
let client_ip = client_ip.clone();
async move {
match authenticate(req).await {
Ok(req) => handle(client_ip, req).await,
Err(_) => {
eprintln!("Authentication failed");
Ok(Response::builder()
.status(hyper::StatusCode::UNAUTHORIZED)
.body(Body::empty())
.unwrap())
}
}
}
}))
}
});
I want to be able to store a bearer token from the response body temporarily for about 900 seconds and be able to use the token whenever another function calls upon it. I have created the basic structure of creating the client and posting the request and getting a token back. Here is what I have so far:
#[derive(Debug, Deserialize)]
struct SecureToken {
access_token: String,
expiration_date_time: i32,
}
#[tokio::main]
async fn get_new_secure_token() {
let mut map = HashMap::new();
map.insert("client_id", "client_id");
map.insert("client_secret", "client_secret");
map.insert("scope", "scope");
map.insert("grant_type", "client_credentials");
let client = Client::new();
let token_request = client
.post("https://gatway.address")
.form(&map)
.send()
.await
.expect("");
//.json::<TokenResponse>()
//.await;
match token_request.status() {
StatusCode::OK => {
let query_result = token_request.json::<SecureToken>().await;
println!("{:#?}", query_result);
},
StatusCode::FORBIDDEN => {
println!("Unable to connect to auth service");
},
StatusCode::BAD_REQUEST => {
let bad_request = token_request.json::<BadRequest>().await;
println!("Error: {:?}", bad_request);
}
s => println!("STATUS CODE: {:?}", s),
};
}
Instead of printing the response, I want it to store the token temporarily for about 900 seconds along the lines of this:
let token_request = client
.post("https://gatway.address")
.form(&map)
.send()
.await
.expect("");
//.json::<TokenResponse>()
//.await;
match token_request.status() {
StatusCode::OK => {
let query_result = token_request.json::<SecureToken>().await;
let token = quer
},
StatusCode::FORBIDDEN => {
println!("Unable to connect to auth service");
},
StatusCode::BAD_REQUEST => {
let bad_request = token_request.json::<BadRequest>().await;
println!("Error: {:?}", bad_request);
}
s => println!("STATUS CODE: {:?}", s),
};
}
But I don't know how to store the token nor add a expiration time to the token.
I'm learning how to code a server in rust that accepts connections from any client with any request. So far my server can respond with any form of data I've tested it with, but there's something keeping me restless. Clients doing post|put|patch requests with file data as body arrive with empty bodies except 'application/x-www-form-urlencoded' data. How can I solve this? Here is my server code(just for learning purpose):
use std::{
net::TcpListener,
io::prelude::*,
thread::{
self,
Scope
}
};
static RESPONSE: [&'static str; 10] = [
"HTTP/1.1 200 Ok",
"Content-Length: 25",
"Content-Type: text/plain",
"Content-Encoding: identity",
"Access-Control-Allow-Methods: *",
"Access-Control-Allow-Origin: *",
"Access-Control-Allow-Headers: *",
"Accept-Encoding: *",
"Accept: */*\r\n",
"Grateful checking on me.\n"
];
fn main() {
let listener = TcpListener::bind("127.0.0.1:8000").unwrap();
println!("Listening at localhost:8000");
thread::scope(|here| handle_connections(here, &listener));
}
fn handle_connections<'scope, 'env>(scope: &'scope Scope<'scope, 'env>, listener: &'env TcpListener) {
let actor = thread::Builder::new().spawn_scoped(scope, move || loop {
match listener.accept() {
Ok((mut client, addr)) => {
println!("Connection from: {}.", addr);
let mut buff = [0; 1024];
client.read(&mut buff).unwrap();
match client.write_all(RESPONSE.join("\r\n").as_bytes()) {
Ok(_) => println!("Send successful."),
Err(err) => eprintln!("Send failed: {}", err),
}
println!("{}\n", String::from_utf8(buff.to_vec()).unwrap()); // for debugging
}
Err(_) => eprintln!("Connection failed or disrupted!"),
}
});
if let Err(err) = actor {
eprintln!("Recovering from: {}", err);
handle_connections(scope, listener);
}
}
I'm trying to use a VRF for Solana to generate random numbers, I'm using CPI to do this. When running the test on Localhost it works fine and generates the numbers.
However when I switch to Devnet it fails with the error:
Transaction simulation failed Custom Error 0x0
I'm using Orao-VRF: Github
My lib.rs code:
use anchor_lang::prelude::*;
use orao_solana_vrf::network_state_account_address;
use orao_solana_vrf::program::OraoVrf;
use orao_solana_vrf::randomness_account_address;
use orao_solana_vrf::state::NetworkState;
use orao_solana_vrf::CONFIG_ACCOUNT_SEED;
use orao_solana_vrf::RANDOMNESS_ACCOUNT_SEED;
use std::mem::size_of;
declare_id!("6ag7tVY7RizWm4xZr7Vv3N4yGio5mqS6H9VFAUFvuMQt");
#[program]
pub mod cflip {
use orao_solana_vrf::cpi::accounts::Request;
use super::*;
pub fn spin_and_pull_the_trigger(
ctx: Context<SpinAndPullTheTrigger>,
force: [u8; 32],
) -> Result<()> {
// Zero seed is illegal in VRF
if force == [0_u8; 32] {
return Err(Error::YouMustSpinTheCylinder.into());
}
// Request randomness.
let cpi_program = ctx.accounts.vrf.to_account_info();
let cpi_accounts = Request {
payer: ctx.accounts.player.to_account_info(),
network_state: ctx.accounts.config.to_account_info(),
treasury: ctx.accounts.treasury.to_account_info(),
request: ctx.accounts.random.to_account_info(),
system_program: ctx.accounts.system_program.to_account_info(),
};
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
orao_solana_vrf::cpi::request(cpi_ctx, force)?;
fn get_random(randomness: &[u8; 64]) -> u8 {
// use only first 8 bytes for simplicyty
let value = &randomness[0..size_of::<u64>()];
return value[0];
}
Ok(())
}
}
#[derive(Accounts)]
#[instruction(force: [u8; 32])]
pub struct SpinAndPullTheTrigger<'info> {
#[account(mut)]
player: Signer<'info>,
/// CHECK:
#[account(
mut,
seeds = [RANDOMNESS_ACCOUNT_SEED.as_ref(), &force],
bump,
seeds::program = orao_solana_vrf::ID
)]
random: AccountInfo<'info>,
/// CHECK:
#[account(mut)]
treasury: AccountInfo<'info>,
#[account(
mut,
seeds = [CONFIG_ACCOUNT_SEED.as_ref()],
bump,
seeds::program = orao_solana_vrf::ID
)]
config: Account<'info, NetworkState>,
vrf: Program<'info, OraoVrf>,
system_program: Program<'info, System>,
}
#[error_code]
pub enum Error {
#[msg("The player is already dead")]
PlayerDead,
#[msg("Unable to serialize a randomness request")]
RandomnessRequestSerializationError,
#[msg("Player must spin the cylinder")]
YouMustSpinTheCylinder,
#[msg("The cylinder is still spinning")]
TheCylinderIsStillSpinning,
}
/// Helper that builds the instruction.
#[cfg(feature = "sdk")]
pub fn spin_and_pull_the_trigger<'a>(
cflip: &'a anchor_client::Program,
vrf: &anchor_client::Program,
) -> std::result::Result<anchor_client::RequestBuilder<'a>, anchor_client::ClientError> {
let seed = rand::random();
// vrf accounts
let network_state_address = network_state_account_address();
let request_address = randomness_account_address(&seed);
let vrf_config = vrf.account::<NetworkState>(network_state_address)?.config;
Ok(cflip
.request()
.accounts(crate::accounts::SpinAndPullTheTrigger {
player: cflip.payer(),
treasury: vrf_config.treasury,
random: request_address,
config: network_state_address,
vrf: orao_solana_vrf::id(),
system_program: anchor_client::solana_sdk::system_program::ID,
})
.args(crate::instruction::SpinAndPullTheTrigger { force: seed }))
}
My test.ts code:
import assert from "assert";
import * as anchor from "#project-serum/anchor";
import { Program, BN, Spl } from "#project-serum/anchor";
import {
Keypair,
PublicKey,
SystemProgram,
LAMPORTS_PER_SOL,
SYSVAR_RENT_PUBKEY,
SYSVAR_INSTRUCTIONS_PUBKEY,
Ed25519Program,
} from "#solana/web3.js";
import { Orao, networkStateAccountAddress, randomnessAccountAddress } from "../js/dist";
import { Cflip } from "../target/types/cflip";
import nacl from "tweetnacl";
describe("cflip", () => {
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const program = anchor.workspace.Cflip as Program<Cflip>;
const vrf = new Orao(provider);
// This accounts are for test VRF.
const treasury = Keypair.generate();
const fulfillmentAuthority = Keypair.generate();
// Initial force for russian-roulette
let force = Keypair.generate().publicKey;
// This helper will play a single round of russian-roulette.
async function spinAndPullTheTrigger(force: Buffer) {
const random = randomnessAccountAddress(force);
await program.methods
.spinAndPullTheTrigger([...force])
.accounts({
player: provider.wallet.publicKey,
vrf: vrf.programId,
config: networkStateAccountAddress(),
treasury: treasury.publicKey,
random,
systemProgram: SystemProgram.programId,
})
.rpc();
}
// This helper will fulfill randomness for our test VRF.
async function emulateFulfill(seed: Buffer) {
let signature = nacl.sign.detached(seed, fulfillmentAuthority.secretKey);
await vrf.methods
.fulfill()
.accounts({
instructionAcc: SYSVAR_INSTRUCTIONS_PUBKEY,
networkState: networkStateAccountAddress(),
request: randomnessAccountAddress(seed),
})
.preInstructions([
Ed25519Program.createInstructionWithPublicKey({
publicKey: fulfillmentAuthority.publicKey.toBytes(),
message: seed,
signature,
}),
])
.rpc();
}
before(async () => {
await provider.connection.confirmTransaction(
await provider.connection.requestAirdrop(treasury.publicKey, 1 * LAMPORTS_PER_SOL),
"confirmed"
);
// Initialize test VRF
const fee = 1 * LAMPORTS_PER_SOL;
const fullfillmentAuthorities = [
fulfillmentAuthority.publicKey,
];
const configAuthority = Keypair.generate();
await vrf.methods
.initNetwork(
new BN(fee),
configAuthority.publicKey,
fullfillmentAuthorities,
null
)
.accounts({
networkState: networkStateAccountAddress(),
treasury: treasury.publicKey,
})
.rpc();
});
it("spin and pull the trigger", async () => {
await spinAndPullTheTrigger(force.toBuffer());
await emulateFulfill(force.toBuffer());
const randomness = await vrf.getRandomness(force.toBuffer());
console.log(randomness.randomness);
});
});
From what I've gathered it's something to do with this before block in the tests file:
before(async () => {
await provider.connection.confirmTransaction(
await provider.connection.requestAirdrop(treasury.publicKey, 1 * LAMPORTS_PER_SOL),
"confirmed"
);
// Initialize test VRF
const fee = 1 * LAMPORTS_PER_SOL;
const fullfillmentAuthorities = [
fulfillmentAuthority.publicKey,
];
const configAuthority = Keypair.generate();
await vrf.methods
.initNetwork(
new BN(fee),
configAuthority.publicKey,
fullfillmentAuthorities,
null
)
.accounts({
networkState: networkStateAccountAddress(),
treasury: treasury.publicKey,
})
.rpc();
});
Any help would be massively appreciated!
Thank you.
VRF program on devnet is already initialized with our config, so you can't initialize it.
The emulateFulfill function will throw UnauthorizedFulfillmentAuthority exception because your fulfillmentAuthority key is not whitelisted.
Try something like this, don't forget to change the program ID with your own cflip program ID
import * as anchor from "#project-serum/anchor";
import { Program } from "#project-serum/anchor";
import {
Keypair,
PublicKey,
SystemProgram,
} from "#solana/web3.js";
import { Orao, networkStateAccountAddress, randomnessAccountAddress } from "#orao-network/solana-vrf";
import { Cflip, IDL } from "../target/types/cflip";
describe("cflip", () => {
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const program = new Program<Cflip>(IDL, new PublicKey("2XeJv53N1UzbupYNDH9PDakRWQFS4VX4bJDPm8P5T64J"), provider);
const vrf = new Orao(provider);
// Initial force for russian-roulette
let force = Keypair.generate().publicKey;
// This helper will play a single round of russian-roulette.
async function spinAndPullTheTrigger(force: Buffer) {
const random = randomnessAccountAddress(force);
const networkState = await vrf.getNetworkState()
const treasury = networkState.config.treasury
await program.methods
.spinAndPullTheTrigger([...force])
.accounts({
player: provider.wallet.publicKey,
vrf: vrf.programId,
config: networkStateAccountAddress(),
treasury,
random,
systemProgram: SystemProgram.programId,
})
.rpc();
}
it("spin and pull the trigger", async () => {
await spinAndPullTheTrigger(force.toBuffer());
// Await fulfilled randomness (default commitment is "finalized"):
const randomness = await vrf.waitFulfilled(force.toBuffer());
console.log("Your randomness is " + randomness.fulfilled());
});
});
So I want to use tokio_postgres in an actix API, I just started and I am having difficulties with sharing the client in my get request.
Here is my code:
use actix_web::{web, App, HttpServer, HttpResponse};
use tokio_postgres::{NoTls};
use std::cell::RefCell;
mod api;
use api::routes::hello_actix;
fn main() {
actix_rt::System::run(|| {
println!("connecting to postgres");
let pro = tokio::spawn(
async
{
let (client , conn) = match tokio_postgres::connect(
"foo",
NoTls)
.await {
Ok(t) => t,
Err(e) => panic!("{}", e)
};
tokio::spawn(async move {
if let Err(e) = conn.await {
eprintln!("connection error: {}", e);
}
});
println!("connected to postgres");
HttpServer::new(|| {
App::new()
.data(RefCell::new(conn))
.route("/hello", web::get().to(hello_actix))
.default_service(web::route().to(|| HttpResponse::NotFound().body("404 Not Found")))
})
.bind("127.0.0.1:8080")
.unwrap()
.run();
Ok(())
});
});
}
i base this code on this but it does not seem to be working.
I would also like to stay "Pure" so I don't want to have a global client if possible.
Is it even possible to do what I want?