laravel "invalid host" on loadbalancer redirects - laravel

Background: I'm working on an api which I host on ec2 servers. I just finish the login and set up an nginx loadbalancer which redirect to the said server's internal ip's. The domain name points to the load balancer.
This used to work well with code igniter, but now I keep getting an "invalid host" problem.
I tried googling it and it found some things about trusted proxies so I installed what fideloper made and tried his post as well (I've followed a guide by fideloper on laravel-4-trusted-proxies and used and tried his trusted sample on github: fideloper/TrustedProxy) but I still get the same error:
UnexpectedValueException
Invalid Host "api.myserver.im, api.myserver.im"
// as the host can come from the user (HTTP_HOST and depending on the configuration, SERVER_NAME too can come from the user)
// check that it does not contain forbidden characters (see RFC 952 and RFC 2181)
if ($host && !preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host)) {
throw new \UnexpectedValueException(sprintf('Invalid Host "%s"', $host));
}
Can someone help me?

I had the same issue as well. I had to resort to modifying the UrlGenerator.php file, which is part of the framework (bad I know...) just to get this to work.
So here's my "temporary" solution.
Create an array value to your app.php config file. e.g:
return array(
'rooturl' => 'https://www.youractualdomainname.com',
...
Next add the below modification in your UrlGenerator.php file <-- (trunk/vendor/laravel/framework/src/Illuminate/Routing/UrlGenerator.php)
<?php namespace Illuminate\Routing;
use Config;
...
protected function getRootUrl($scheme, $root = null)
{
$approoturl = Config::get('app.rooturl');
$root = isset($approoturl) ? $approoturl : $this->request->root();
return $root;
// if (is_null($root))
// {
// $root = $this->forcedRoot ?: $this->request->root();
// }
// $start = starts_with($root, 'http://') ? 'http://' : 'https://';
// return preg_replace('~'.$start.'~', $scheme, $root, 1);
}
Do note that composer update will revert your modification.

Related

Azure functions The remote certificate is invalid according to the validation procedure

I have created two Azure httpTrigger functions and serve them over https. During local development when I call azure function 2 from azure function 1 I get the following message:
The SSL connection could not be established, see inner exception.
The remote certificate is invalid according to the validation procedure.
After looking for a solution I found this (solution 1) and this (solution 2)
I tried the first solution (shown below) and it did not make a difference (Aside: I'm glad as I don't like removing the security checks for a call)
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, errors) =>
{
var isDevelopment = false;
#if DEBUG
isDevelopment = true;
#endif
if (isDevelopment) return true;
return errors == SslPolicyErrors.None;
};
I considered solution 2 but when my application starts up it clearly states:
Generating a self signed certificate using openssl
My question is how do I call azure function 2 from azure function 1 without disabling ServerCertificateValidationCallback
UPDATE:
I created a certificate manually and it continued to return the same error. I have managed to supress the error for local development by replacing ServicePointManager.ServerCertificateValidationCallback with ConfigurePrimaryHttpMessageHandler when I set up my httpClient. Which now looks like below. But I would still like to know how to make the call without this being needed
services.AddHttpClient<ILocationDetailsService, LocationDetailsService>(client =>
{
var writeBaseUrl = configuration.GetValue<string>("WriteBaseUrl");
client.BaseAddress = new Uri(writeBaseUrl); // get url from config
client.DefaultRequestHeaders.Add("ContentType", "application/json");
})
.ConfigurePrimaryHttpMessageHandler(() =>
new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => {
var isDevelopment = false;
#if DEBUG
isDevelopment = true;
#endif
if (isDevelopment) return true;
return sslPolicyErrors == SslPolicyErrors.None;
}
}
)
UPDATE 2:
#John Wu has suggested that I identify the error by navigating to the url in the browser. In firefox I get:
https://localhost:7072/api/contact
The certificate is not trusted because it is self-signed.
Error code: MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT
In chrome I get
NET::ERR_CERT_AUTHORITY_INVALID
Looks like I have my answer. Once I resolve it I will update with and answer. On a side note, it looks like all my endpoint are doing the same, I had been assuming that they were all working without errors until now. Thanks #John Wu

How do I inject Heroku's PORT value into a dotnet core 6.0 web api?

I'm not using Docker. Rather, I'm trying to use the jincod/dotnetcore buildpack. However, I need a way to tell dotnet to use Heroku's port number environment variable, and I just don't see a good way of doing that here.
In my Program.cs file, I added the following:
builder.Services.AddHttpsRedirection(options =>
{
options.HttpsPort = int.TryParse(Environment.GetEnvironmentVariable("PORT"), out var p) ? p : null;
});
which actually sort of works in that I'm able to obtain the port number value from Heroku, but this just redirects me to myapp.heroku.com:1234 from myapp.heroku.com and is still not working as expected.
I see in the launchSettings.json file, in the profiles section, there is an applicationUrl that shows two urls, one for https and one for http, both of which have port numbers specified. I think if I can overwrite those port numbers this might work, but I don't know how to inject that environment variable into the launchSettings.json file from Heroku. Anyone know a good way of doing this? Or am I even on the right track here?
I believe if you add the following before the https redirection middleware you won't need to pass any configuration into UseHttpsRedirection
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedProto
});
You can get the port variable and bind it. If you are using dotnet 6 minimal APIs you can do this.
var port = Environment.GetEnvironmentVariable("PORT") ?? "3100";
app.Run("http://0.0.0.0:" + port);
You'll need to add "https_port": 443 to your appsettings.json or set the environment variable ASPNETCORE_HTTPS_PORT to 443 in heroku. You can see this in action here.
https://github.com/TerribleDev/dotnet-mvc-test
https://tp-aspnet-tst.herokuapp.com/
If you are using older versions than 6 you can do something like this...
public static IWebHostBuilder CreateWebHostBuilder(string[] args) {
var builder = WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureKestrel(a =>
{
a.AddServerHeader = false;
});
var port = Environment.GetEnvironmentVariable("PORT");
if(!String.IsNullOrWhiteSpace(port)) {
builder.UseUrls("http://*:" + port);
}
return builder;
}

Laravel Paypal SandBox to Live

My project works perfectly in mode 'sandbox' but when I go to put it in mode 'live' (I did it correctly as indicated in the instructions in PayPal-PHP-SDK, my credentials are correct and I put mode 'live' instead of mode 'sandbox').
It gives me the following error:
PayPal \ Exception \ PayPalConnectionException
Got Http response code 401 when accessing https://api.sandbox.paypal.com/v1/oauth2/token.
Looking for this error notice that it happened to others but it was the following error:
PayPal \ Exception \ PayPalConnectionException
Got Http response code 401 when accessing https://api.paypal.com/v1/oauth2/token.
Why does my error say sandbox if I have mode 'live'? So I started looking because if I had mode 'live' I kept getting api.sandbox.paypal.com instead of api.paypal.com error.
and get to vendor \ paypal \ rest-api-sdk-php \ lib \ PayPal \ Handler \ OauthHandler.php (Which I have never modified) has a _getEndPoint method.
private static function _getEndpoint($config)
{
if (isset($config['oauth.EndPoint'])) {
$baseEndpoint = $config['oauth.EndPoint'];
} elseif (isset($config['service.EndPoint'])) {
$baseEndpoint = $config['service.EndPoint'];
} elseif (isset($config['mode'])) {
switch (strtoupper($config['mode'])) {
case 'SANDBOX':
$baseEndpoint = PayPalConstants::REST_SANDBOX_ENDPOINT;
break;
case 'LIVE':
$baseEndpoint = PayPalConstants::REST_LIVE_ENDPOINT;
break;
default:
throw new PayPalConfigurationException('The mode config parameter must be set to either sandbox/live');
}
} else {
// Defaulting to Sandbox
$baseEndpoint = PayPalConstants::REST_SANDBOX_ENDPOINT;
}
$baseEndpoint = rtrim(trim($baseEndpoint), '/') . "/v1/oauth2/token";
return $baseEndpoint;
}
I noticed that the config always arrives empty in that part, when it arrives empty it goes to the case by default
which is sandbox. That's why sandobox works for me even though nothing is coming either.
Any idea why this can happen. I really have no idea, any help is welcome.
I think you need mode 'production' for that SDK's config
But you are using a deprecated v1 SDK that is no longer maintained
You should be using the v2 Checkout-PHP-SDK, documented here: https://developer.paypal.com/docs/checkout/reference/server-integration/
By the way, the best UI approval flow to pair it with is here: https://developer.paypal.com/demo/checkout/#/pattern/server
I have a solution, it is not a very elegant one but it works for me. In vendor\paypal\rest-api-sdk-php\lib\PayPal\Core\PayPalsConstants.php change the variable REST_SANDBOX_ENDPOINT from "http://api.sandbox.paypal.com" to "http://api.paypal.com"

Modify RewriteContext Host in NetCore 2 rewriting middleware

I need to rewrite not redirect certain requests to GET files to an external storage location (hosted on a different domain)
I am able to accomplish this with redirecting using rewriter middleware, however i would like to rewrite instead.
I need to use some logic to rewrite to a specific file location, so I followed https://learn.microsoft.com/en-us/aspnet/core/fundamentals/url-rewriting?view=aspnetcore-2.0&tabs=aspnetcore2x#method-based-rule tutorial.
When i set the Host and path i can see the absolute url looks correct but when the method exits i get "No webpage was found for the web address:" and url displayed is the original request Url
RewriteRule.cs (also holds redirect url logic in ApplyRule() that i don't want to use)
public static void RewriteStorageFileRequests(RewriteContext context)
{
var request = context.HttpContext.Request;
var path = request.Path.Value;
if (path.Contains("foo/bar"))
{
context.Result = RuleResult.SkipRemainingRules;
request.Host = new HostString("storage/space");
request.Path = path.Replace("foo/bar", string.Empty);
var test2 = request.GetDisplayUrl();
}
}
So essentially, if a call comes in as:
https://localhost:44327/foo/bar/bf34d911-03db-5tda-9c99-c7dd96593159/w3.jpg
I want to rewrite it to:
https://storage/space/bf34d911-03db-5tda-9c99-c7dd96593159/w3.jpg
Startup.cs
app.UseRewriter(new RewriteOptions().Add(RewriteUrlRule.RewriteStorageFileRequests));
test2 displays the correct url in the debugger.
I tried setting host to empty string and passing host value (storage/space) in path but still no luck.
Before Rewriter
After Rewrite
UPDATE: I was able to get rewrite to work by changing
context.Result = RuleResult.SkipRemainingRules;
to
context.Result = RuleResult.EndResponse;
However, when i get to the Url it returns 200OK but does not show the image.
(see attached file)
So, right now the only way i can get images is still with Redirect
Rewrite GET

Keep getting redis error on Amazon EC2

I just moved my Magento site to Amazon EC2, but keep getting "Connection to Redis failed after 2 failures" error. I've tried to remove the redis configuration from app/etc/local.xml but still get that error.
I also tried to disable all the cache options directly from core_cache_option table. I have no idea how to clean the already cached files. No cache files under var/cache folder as expected and I've tried to flushall from redis-cli command prompt, but still keep getting this error.
Any idea what else should I try?
<cache>
<backend_options>
<server><![CDATA[/var/tmp/_cache.sock]]></server>
<port><![CDATA[0]]></port>
<persistent><![CDATA[]]></persistent>
<database><![CDATA[0]]></database>
<password><![CDATA[]]></password>
<connect_retries><![CDATA[1]]></connect_retries>
<read_timeout><![CDATA[10]]></read_timeout>
<automatic_cleaning_factor><![CDATA[0]]></automatic_cleaning_factor>
<compress_data><![CDATA[1]]></compress_data>
<compress_tags><![CDATA[1]]></compress_tags>
<compress_threshold><![CDATA[20480]]></compress_threshold>
<compression_lib><![CDATA[gzip]]></compression_lib>
<use_lua><![CDATA[0]]></use_lua>
</backend_options>
<backend><![CDATA[Cm_Cache_Backend_Redis]]></backend>
</cache>
Given EC2 instances are ephemeral, you should be able to regenerate the instance, right? If that's not an option —
First, Check app/etc/ for other XML files. Magento will parse any XML files it finds in this folder. I've seen something like the following trip people up
$ ls app/etc/*.xml
local.xml
local.backup.xml
Magento parses both local.xml and local.backup.xml, and the backup values override the new values in local.xml. Also, make sure you're working with the local.xml you think you are. Magento loads the local configuration in the following location. Add some temporary debugging to make sure it's doing what you think it's doing.
#File: app/code/core/Mage/Core/Model/Config.php
public function loadBase()
{
$etcDir = $this->getOptions()->getEtcDir();
$files = glob($etcDir.DS.'*.xml');
$this->loadFile(current($files));
while ($file = next($files)) {
var_dump($file);
$merge = clone $this->_prototype;
$merge->loadFile($file);
$this->extend($merge);
}
if (in_array($etcDir.DS.'local.xml', $files)) {
$this->_isLocalConfigLoaded = true;
}
return $this;
}
Second, after you clear your cache, make sure Magento's reloading the configuration. Add some temporary debugging to
#File: app/code/core/Mage/Core/Model/Config.php
public function init($options=array())
{
$this->setCacheChecksum(null);
$this->_cacheLoadedSections = array();
$this->setOptions($options);
$this->loadBase();
$cacheLoad = $this->loadModulesCache();
if ($cacheLoad) {
var_dump("Loaded Config from Cache");
return $this;
}
else
{
var_dump("Reloading configuration");
}
$this->loadModules();
$this->loadDb();
$this->saveCache();
return $this;
}
Finally, if you suspect the problem is a file based cache not clearing, drop some debugging code in
#File: app/code/core/Mage/Core/Model/Config/Options.php
public function getCacheDir()
{
//$dir = $this->getDataSetDefault('cache_dir', $this->getVarDir().DS.'cache');
$dir = $this->_data['cache_dir'];
$this->createDirIfNotExists($dir);
var_dump($dir);
return $dir;
}
This will let you know the cache directory Magento's reading from — if Magento can't read the local var, it'll pop up to the root level /var/ folder.

Resources