I'm facing some issues while dealing with certificates in terraform.
Before writing the code below, i've already made a CSR request.
I need to say that certificate_pem and private_key are both encoded in base64, particularly private_key is encrypted.
In the code below, i would like to use private_key and certificate_pem.
resource "kubernetes_secret" "my-secret" {
data = {
"tls.crt" = data.my_data.my-configuration-secret.data["certificate_pem"]
"tls.key" = data.my_data.my-configuration-secret.data["private_key"]
}
metadata {
name = "my-secret"
namespace = "my-namespace"
}
}
Now, in the Ingress ressource, i use this secret name
resource "kubernetes_ingress" "my-sni" {
metadata {
name = "my-sni"
namespace = "my_namespace"
annotations = {
"kubernetes.io/ingress.class" = "my_namespace"
"kubernetes.io/ingress.allow-http" = "true"
"nginx.ingress.kubernetes.io/ssl-redirect" = "false"
"nginx.ingress.kubernetes.io/force-ssl-redirect" = "false"
"nginx.ingress.kubernetes.io/ssl-passthrough" = "false"
"nginx.ingress.kubernetes.io/secure-backends" = "false"
"nginx.ingress.kubernetes.io/proxy-body-size" = "0"
"nginx.ingress.kubernetes.io/proxy-read-timeout" = "3600000"
"nginx.ingress.kubernetes.io/rewrite-target" = "/$1"
"nginx.ingress.kubernetes.io/proxy-send-timeout" = "400000"
"nginx.ingress.kubernetes.io/backend-protocol" = "HTTP"
}
}
spec {
tls {
hosts = ["my_host"]
secret_name = "my-secret"
}
rule {
host = "my_host"
http {
path {
path = "/?(.*)"
backend {
service_name = "my-service"
service_port = 8080
}
}
}
}
}
}
Everything is fine with terraform apply, but i can't go on the host to check if i can access to the microservice.
Someone told me i've to uncypher the private_key.
I don't know how to do that
I am trying to add an authentication provider to a Pulumi WebApp but is totaly unclear to me how to achieve that. The class WebApp from package #pulumi/azure-native/web only offers the property identity but no property tho assign e.g. a Microsoft AD. Can anybody provide a hint on how to set this up?
There are some of the Pulumi Azure pre-requisites and have the appropriate permissions in your tenant and Azure subscription.
Follow the below steps to add the authentication to app service webapp with pulumi and deploy:
Creating the project:
Start by creating the application, and adding the AzureAD package we’ll need to create the Azure AD application registration.
pulumi new azure-csharp `
--name easyauth-webapp `
--description "azure ad secured app" `
--stack dev `
--config azure-native:location=eastus
dotnet add package Pulumi.AzureAD
We next need to update the contents of the pulumi.dev.yaml file to contain a few additional config items. Paste the following into the file:
config:
azure-native:location: eastus
azure-native:subscriptionId: UPDATE_ME
azure-native:tenantId: UPDATE_ME
easyauth-webapp:tenantId: UPDATE_ME
easyauth-webapp:ownerId: UPDATE_ME
easyauth-webapp:siteName: UPDATE_ME
easyauth-webapp:appRegistrationName: UPDATE_ME
You can set siteName and appRegistrationName to whatever you want.
The subscriptionId and tenantId should be set to the appropriate target’s for your Azure app service and Azure AD application registration, respectively.
The following commands may be helpful in retrieving these values:
# Get your user's id
az ad signed-in-user show --query objectId
# List all subscriptions (and their tenant) that you have access to
az account list
Deploy the website (with no security):
We’ll next create the website we want to deploy. We’re going to use the run from ZIP package functionality to deploy the contents of the wwwroot folder.
Create that folder and add some content to the index.htm file:
Ex:
<!-- wwwroot/index.htm -->
<html>
<head>
<title>A very secure app</title>
</head>
<body>
Hello EasyAuth with Pulumi!
</body>
</html>
Now we can deploy this file to Azure with Pulumi.
Modify the MyStack.cs file to contain the below code, which has been adapted from the Pulumi Function Stack example:
// MyStack.cs
using System;
using Pulumi;
using Pulumi.AzureAD;
using Pulumi.AzureAD.Inputs;
using Pulumi.AzureNative.Resources;
using Pulumi.AzureNative.Storage;
using Pulumi.AzureNative.Storage.Inputs;
using Pulumi.AzureNative.Web;
using Pulumi.AzureNative.Web.Inputs;
class MyStack : Stack
{
public MyStack()
{
var config = new Pulumi.Config();
var tenantId = config.Require("tenantId");
var ownerId = config.Require("ownerId");
var siteName = config.Require("siteName");
var appRegistrationName = config.Require("appRegistrationName");
var rg = new ResourceGroup($"RG-{siteName}");
var storageAccount = new StorageAccount("storageaccount", new StorageAccountArgs
{
ResourceGroupName = rg.Name,
Kind = "StorageV2",
Sku = new SkuArgs
{
Name = SkuName.Standard_LRS,
},
});
var appServicePlan = new AppServicePlan("appserviceplan", new AppServicePlanArgs
{
ResourceGroupName = rg.Name,
Kind = "App",
Sku = new SkuDescriptionArgs
{
Tier = "Basic",
Name = "B1",
},
});
var container = new BlobContainer("zips", new BlobContainerArgs
{
AccountName = storageAccount.Name,
PublicAccess = PublicAccess.None,
ResourceGroupName = rg.Name,
});
var blob = new Blob("appservice-blob", new BlobArgs
{
ResourceGroupName = rg.Name,
AccountName = storageAccount.Name,
ContainerName = container.Name,
Type = BlobType.Block,
Source = new FileArchive("wwwroot"),
});
var codeBlobUrl = SignedBlobReadUrl(blob, container, storageAccount, rg);
var app = new WebApp("app", new WebAppArgs
{
Name = siteName,
ResourceGroupName = rg.Name,
ServerFarmId = appServicePlan.Id,
SiteConfig = new SiteConfigArgs
{
AppSettings = {
new NameValuePairArgs{
Name = "WEBSITE_RUN_FROM_PACKAGE",
Value = codeBlobUrl,
}
},
}
});
this.Endpoint = app.DefaultHostName;
}
// From https://github.com/pulumi/examples/blob/master/azure-cs-functions/FunctionsStack.cs
private static Output<string> SignedBlobReadUrl(Blob blob, BlobContainer container, StorageAccount account, ResourceGroup resourceGroup)
{
return Output.Tuple<string, string, string, string>(
blob.Name, container.Name, account.Name, resourceGroup.Name).Apply(t =>
{
(string blobName, string containerName, string accountName, string resourceGroupName) = t;
var blobSAS = ListStorageAccountServiceSAS.InvokeAsync(new ListStorageAccountServiceSASArgs
{
AccountName = accountName,
Protocols = HttpProtocol.Https,
SharedAccessStartTime = "2021-01-01",
SharedAccessExpiryTime = "2030-01-01",
Resource = SignedResource.C,
ResourceGroupName = resourceGroupName,
Permissions = Permissions.R,
CanonicalizedResource = "/blob/" + accountName + "/" + containerName,
ContentType = "application/json",
CacheControl = "max-age=5",
ContentDisposition = "inline",
ContentEncoding = "deflate",
});
return Output.Format($"https://{accountName}.blob.core.windows.net/{containerName}/{blobName}?{blobSAS.Result.ServiceSasToken}");
});
}
[Output] public Output<string> Endpoint { get; set; }
}
We can now deploy the site and verify it has worked as intended:
pulumi up --stack dev
curl (pulumi stack --stack dev output Endpoint)
[
Securing the site:
To configure Easy Auth we first create an Azure AD application registration.
In this example I’m specifying AzureADMyOrg which restricts access to the tenant the application registration is deployed in. I’m also adding a RedirectUri that points at the Easy Auth middleware of the deployed site. A password is needed to use as a client secret (the web application being the client in this case).
Once the application registration is created we can add WebAppAuthSettings to our site. The example specifies no anonymous access (using RedirectToLoginPage), and connects the site to the application registration using the ClientId and ClientSecret (password).
Paste the below code just after the this.Endpoint... code in the above MyStack.cs:
// MyStack.cs
// After this.Endpoint = app.DefaultHostName;
var adApp = new Application("ADAppRegistration", new ApplicationArgs
{
DisplayName = appRegistrationName,
SignInAudience = "AzureADMyOrg",
Owners = new[] { ownerId },
Web = new ApplicationWebArgs
{
ImplicitGrant = new ApplicationWebImplicitGrantArgs
{
IdTokenIssuanceEnabled = true
},
RedirectUris = new System.Collections.Generic.List<string> { $"https://{siteName}.azurewebsites.net/.auth/login/aad/callback" }
}
}
);
var applicationPassword = new ApplicationPassword("appPassword", new ApplicationPasswordArgs
{
ApplicationObjectId = adApp.Id,
DisplayName = "Client secret for web app"
});
var allowedAudience = adApp.ApplicationId.Apply(id => $"api://{id}");
var authSettings = new WebAppAuthSettings("authSettings", new WebAppAuthSettingsArgs
{
ResourceGroupName = rg.Name,
Name = app.Name,
Enabled = true,
UnauthenticatedClientAction = UnauthenticatedClientAction.RedirectToLoginPage,
DefaultProvider = BuiltInAuthenticationProvider.AzureActiveDirectory,
ClientId = adApp.ApplicationId,
ClientSecret = applicationPassword.Value,
Issuer = $"https://sts.windows.net/{tenantId}/v2.0",
AllowedAudiences = new[] { allowedAudience },
});
We can now update the site, From the command line we can’t get much further than this.
But in a browser we’ll get redirected to complete the login flow and access the site.
pulumi up --stack dev
# Redirect to HTTPS
curl (pulumi stack --stack dev output Endpoint)
# Access denied
curl "https://$(pulumi stack --stack dev output Endpoint)"
Refer this Github link for pulumi samples.
I'm using Ktor client to make calls to an API and I didn't find any examples of how to construct a URL with query parameters.
I wanted something like this:
protocol = HTTPS,
host = api.server.com,
path = get/items,
queryParams = List(
Pair("since", "2020-07-17"),
)
I can't find any examples of how to use URL builder for this.
If you want to specify each of this element (protocol, host, path and params) separately you can use a HttpClient.request method to construct your url. Inside this method you have access to HttpRequestBuilder and then you can configure url with usage of UrlBuilder
client.request<Response> {
url {
protocol = URLProtocol.HTTPS
host = "api.server.com"
path("get", "items")
parameters.append("since", "2020-07-17")
}
}
Response type is your response, you can specify there whatever you need
It would also be helpful if someone wants to add a base URL to all their requests :
HttpClient(Android) {
expectSuccess = false
//config Client Serialization
install(JsonFeature) {
serializer = KotlinxSerializer(json)
}
//config client logging
install(Logging) {
level = LogLevel.BODY
}
//Config timeout
install(HttpTimeout) {
requestTimeoutMillis = 30 * 1000L
connectTimeoutMillis = 10 * 1000L
}
//Config Base Url
defaultRequest {
url {
protocol =URLProtocol.HTTPS
host = baseUrl
}
}
}
val json = kotlinx.serialization.json.Json {
ignoreUnknownKeys = true
isLenient = true
encodeDefaults = false
}
The code I use currently on my website
var client = null;
var device_is_on = null;
var hostname = "********";
var port = "8003";
var clientId = "mqtt_js_" + parseInt(Math.random() * 100000, 10);
var device_topic = "stat/Device_001/POWER";
var status_topic = "cmnd/Device_001/power";
function connect(){
client = new Paho.MQTT.Client(hostname, Number(port), clientId);
client.onConnectionLost = onConnectionLost;
client.onMessageArrived = onMessageArrived;
var options = {
useSSL: true,
userName : "***",
password : "********",
onSuccess: onConnect,
onFailure: onFail
};
client.connect(options);
}
function onConnect(context) {
options = {qos:0}
client.subscribe(device_topic, options);
client.subscribe(status_topic, options);
var payloadd = "6";
message = new Paho.MQTT.Message(payloadd);
message.destinationName = status_topic;
message.retained = true;
client.send(message);
}
function onFail(context) {
}
function onConnectionLost(responseObject) {
if (responseObject.errorCode !== 0) {
window.alert("Connection Lost!\nPlease Refresh.");
}
}
function onMessageArrived(message) {
if (message.destinationName == device_topic){
var temperature_heading = document.getElementById("device_display");
temperature_heading.innerHTML = "Air Conditioner: " + message.payloadString;
if (message.payloadString == "ON" || message.payloadString == "o"){
device_is_on = true;
} else {
device_is_on = false;
}
}
}
function device_toggle(){
if (device_is_on){
var payload = "off";
device_is_on = false;
} else {
var payload = "on";
device_is_on = true;
}
message = new Paho.MQTT.Message(payload);
message.destinationName = status_topic;
message.retained = true;
client.send(message);
}
What should I put under the "" var options "" section? currently I am getting the error ERR_CERT_AUTHORITY_INVALID in the console of Google Chrome.
Note 1: This code functions perfectly over http but I am converting to https.
Note 2: I use Mosquitto as my MQTT broker.
Help in much appreciated.
It looks like you are using a self signed certificate. This will not be trusted by your browser so it will not connect, raising the error you have shown.
You have 2 options:
Import the certificate into your browser and mark it as trusted (how you do this will vary depending on what browser you are using). This is only really useful for testing/development because normal users should not be importing random certificates as this opens them up to all kinds of security problems.
Get a real trusted certificate for your website and broker. The simplest/cheapest way to do this will be to use letsencrypt. You can then configure mosquitto to use this certificate.
TLS javascript paho client is available: Github paho.mqtt.javascript/issues/88
I've got the following code to handle client side navigation using HTML5 pushstate (classic combination of crossroadsjs and historyjs):
History = window.History;
History.Adapter.bind(window, 'statechange', function () {
var state = History.getState();
console.log(state);
if (state.data.urlPath) {
return crossroads.parse(state.data.urlPath);
}
else
{
if (state.hash.length > 1) {
var fullHash = state.hash;
var hashPath = fullHash.slice(0, fullHash.indexOf('?'));
return crossroads.parse(hashPath);
}
}});
crossroads.normalizeFn = crossroads.NORM_AS_OBJECT;
crossroads.parse('/');
$('body').on('click', 'a', function(e) {
var title, urlPath;
urlPath = $(this).attr('href');
if (urlPath.slice(0, 1) == '#'){
return true;
}
e.preventDefault();
title = $(this).text().trim();
return History.pushState({ urlPath: urlPath }, title, urlPath);
});
It works really well. Now, to handle url bookmarking and sharing, I added and express server to handle all requests. All it does is to redirect to index.html (a sort of catchall rule):
var env = require('./env');
var fallback = require('express-history-api-fallback');
var express = require('express');
var app = express();
var config = env.config();
var root = __dirname + '/dist';
app.use(express.static(root));
app.use(fallback('index.html', { root: root }));
var port = process.env.PORT || 9090;
var server = app.listen(port, function () {
console.log('Server started at: http://localhost:' + port);
console.log(config);
});
The problem I am facing is that it successfully redirects to index.html but it doesn't load the correct route on the client side. So a request to www.mysite.com or www.mysite.com/anotherpage will always load the home page route.
I am obviously missing some code to intercept that and load the appropriate route on the client side. I just don't know what to do.
Found where the bug was:
crossroads.parse('/');
This was always redirecting to the "home" route. I just had to refactor the code a bit:
History.Adapter.bind(window, 'statechange', this.routeCrossRoads);
routeCrossRoads() {
var state = History.getState();
if (state.data.urlPath) {
return crossroads.parse(state.data.urlPath);
}
else {
if (state.hash.length > 1) {
var fullHash = state.hash;
var pos = fullHash.indexOf('?');
if (pos > 0) {
var hashPath = fullHash.slice(0, pos);
return crossroads.parse(hashPath);
}
else {
return crossroads.parse(fullHash);
}
}
else {
return crossroads.parse('/');
}
}
}