I have some dockers containers deployed on AWS EC2, that listens on http.
My idea is using nginx as reverse proxy, to pass traffic from https, to http://localhost.
Each container listens on a specific http port. The EC2 instance will accept traffic just on http:80 and http:443 and I will use subdomain to chose the right port.
So I should have:
https://backend.my_ec2instance.com --> http://localhost:4000
https://frontend.my_ec2instance.com --> http://localhost:5000
I'v got my free TSL certificate from Let's Encrypt (it's just on file containing public and private keys), and put it in
/etc/nginx/ssl/letsencrypt.pem
Then I have configured nginx in this way
sudo nano /etc/nginx/sites-enabled/custom.conf
and wrote
server {
listen 443 ssl;
server_name backend.my_ec2instance;
# Certificate
ssl_certificate letsencrypt.pem;
# Private Key
ssl_certificate_key letsencrypt.pem;
# Forward
location / {
proxy_pass http://localhost:4000;
}
}
server {
listen 443 ssl;
server_name frontend.my_ec2instance;
# Certificate
ssl_certificate letsencrypt.pem;
# Private Key
ssl_certificate_key letsencrypt.pem;
# Forward
location / {
proxy_pass http://localhost:5000;
}
}
then
sudo ln -s /etc/nginx/sites-available/custom.conf /etc/nginx/sites-enbled/
Anyway, if I open my browser on https://backend.my_ec2instance it's not reachable.
http://localhost:80 instead correctly shows the nginx page.
HTTPS default port is port 443. HTTP default port is port 80. So this: https://localhost:80 makes no sense. You are trying to use the HTTPS protocol on an HTTP port.
In either case, I don't understand why you are entering localhost in your web browser at all. You should be trying to open https://frontend.my_ec2instance.com in your web browser. The locahost address in your web browser would refer to your local laptop, not an EC2 server.
Per the discussion in the comments you also need to include your custom Nginx config in the base configuration file.
I have built a Websocket on Ubuntu 20.04 (LAMP) stack using Ratchet.
I followed this article https://www.twilio.com/blog/create-php-websocket-server-build-real-time-even-driven-application to build the websocket.
I Followed this article Does an Application Load Balancer support WebSockets? to configure my webserver.
Configured Security Group - Inbound rules TCP 8080
Configured Load balancer
Created Target group (TCP) for port 8080
Enabled Stickyness (1 hour)
SSL is configured and created in AWS Certificate Manager
Apache configuration
<VirtualHost *:80>
ServerName chat.domain.com
ServerAdmin webmaster#localhost
DocumentRoot /var/www/websites/chat.domain.com/public
<Directory /var/www/websites/chat.domain.com/public/>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/chat.domain.com/error.log
CustomLog ${APACHE_LOG_DIR}/chat.domain.com/access.log combined
</VirtualHost>
<VirtualHost *:443>
ServerName chat.domain.com
ServerAdmin webmaster#localhost
DocumentRoot /var/www/websites/chat.domain.com/public
<Directory /var/www/websites/chat.domain.com/public/>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Require all granted
</Directory>
ProxyPass / https://chat.domain.com:8080/
ProxyPassReverse / https://chat.domain.com:8080/
<Location "/">
ProxyPass "wss://chat.domain.com:8080/"
</Location>
ErrorLog ${APACHE_LOG_DIR}/chat.domain.com/error.log
CustomLog ${APACHE_LOG_DIR}/chat.domain.com/access.log combined
</VirtualHost>
Every thing is in place and running but I get
WebSocket connection to 'wss://chat.domain.com:8080/' failed:
Here is my chrome inspect
I have even tried to Open traffic to ALL ports (Inbond) just to check the security group but still getting the same error.
I doubt the problem is configuring Load Balancer, Security Group and Target group?
Any help or suggestion?
Finally I found the solution. The port 8080 should have been added to firewall and I had to create a new target group for port 8080 which then I had to create a load balancer with port http:8080 and point to the target group.
I've setup a Apache reverse proxy(which receives traffic from outside world via a firewall) with the below configuration:
<VirtualHost *:443>
ServerName xyz.example.com
ProxyRequests Off
ProxyPass / https://internal-voyager-dev-1960104633.us-east-1.elb.amazonaws.com/
ProxyPassReverse / https://internal-voyager-dev-1960104633.us-east-1.elb.amazonaws.com
SSLProxyEngine on
SSLProxyVerify none
SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
SSLEngine on
SSLOptions +StrictRequire
SSLProtocol All -SSLv3 -SSLv2 -TLSv1 -TLSv1.1
.
.
>
This reverse proxy is pointing to a AWS ALB on listener port 443. So the ALB then processes the request based on the rule where HOST(xyz.example.com) is mapped to a target group. But This is not working, I am getting a 502 bad gateway error.
.
If I make config changes like pointing reverse proxy to a http://alb-cname and use the listener port 80 of AWS ALB then I am able to bring up the application but as we use a rails application we get the error saying HTTP Origin header didn't match request.base_url
Appreciate any ideas as to how I can solve this issue.
I have an airflow web server configured at EC2, it listens at port 8080.
I have an AWS ALB(application load balancer) in front of the EC2, listen at https 80 (facing internet) and instance target port is facing http 8080.
I cannot surf https://< airflow link > from browser because the airflow web server redirects me to http : //< airflow link >/admin, which the ALB does not listen at.
If I surf https://< airflow link > /admin/airflow/login?next=%2Fadmin%2F from browser, then I see the login page because this link does not redirect me.
My question is how to change airflow so that when surfing https://< airflow link > , airflow web server will redirect me to https:..., not http://.....
so that AWS ALB can process the request.
I tried to change base_url of airflow.cfg from http://localhost:8080 to https://localhost:8080, according to the below answer, but I do not see any difference with my change....
Anyway, how to access https://< airflow link > from ALB?
Since they're using Gunicorn - you can configure the forwarded_allow_ips value as an evironment variable instead of having to use an intermediary proxy like Nginx.
In my case I just set FORWARDED_ALLOW_IPS = * and it's working perfectly fine.
In ECS you can set this in the webserver task configuration if you're using one docker image for all the Airflow tasks (webserver, scheduler, worker, etc.).
Finally I found a solution myself.
I introduced a nginx reverse proxy between ALB and airflow web server: ie.
https request ->ALB:443 ->nginx proxy: 80 ->web server:8080. I make the nginx proxy tell the airflow web server that the original request is https not http by adding a http header "X-Forwarded-Proto https".
The nginx server is co-located with the web server. and I set the config of it as /etc/nginx/sites-enabled/vhost1.conf (see below). Besides, I deletes the /etc/nginx/sites-enabled/default config file.
server {
listen 80;
server_name <domain>;
index index.html index.htm;
location / {
proxy_pass_header Authorization;
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Connection "";
proxy_buffering off;
client_max_body_size 0;
proxy_read_timeout 36000s;
}
}
User user389955 own solution is probably the best approach, but for anyone looking for a quick fix (or want a better idea on what is going on), this seems to be the culprit.
In the following file (python distro may differ):
/usr/local/lib/python3.5/dist-packages/gunicorn/config.py
The following section prevents forwarded for headers from anything other than local
class ForwardedAllowIPS(Setting):
name = "forwarded_allow_ips"
section = "Server Mechanics"
cli = ["--forwarded-allow-ips"]
meta = "STRING"
validator = validate_string_to_list
default = os.environ.get("FORWARDED_ALLOW_IPS", "127.0.0.1")
desc = """\
Front-end's IPs from which allowed to handle set secure headers.
(comma separate).
Set to ``*`` to disable checking of Front-end IPs (useful for setups
where you don't know in advance the IP address of Front-end, but
you still trust the environment).
By default, the value of the ``FORWARDED_ALLOW_IPS`` environment
variable. If it is not defined, the default is ``"127.0.0.1"``.
"""
Changing from 127.0.0.1 to specific IP's or * if IP's unknown will do the trick.
At this point, I haven't found a way to set this parameter from within airflow config itself. If I find a way, will update my answer.
We solved this problem in my team by adding an HTTP listener to our ALB that redirects all HTTP traffic to HTTPS (so we have an HTTP listener AND an HTTPS listener). Our Airflow webserver tasks still listen on port 80 for HTTP traffic, but this HTTP traffic is only in our VPC so we don't care. The connection from browser to the load balancer is always HTTPS or HTTP that gets rerouted to HTTPS and that's what matters.
Here is the Terraform code for the new listener:
resource "aws_lb_listener" "alb_http" {
load_balancer_arn = aws_lb.lb.arn
port = 80
protocol = "HTTP"
default_action {
type = "redirect"
redirect {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301"
}
}
}
Or if you're an AWS console kinda place here's how you set up the default action for the listener:
Console
I think you have everything working correctly. The redirect you are seeing is expected as the webserver is set to redirect from / to /admin. If you are using curl, you can pass the flag -L / --location to follow redirects and it should bring you to the list of DAGs.
Another good endpoint to test on is https://<airflow domain name>/health (with no trailing slash, or you'll get a 404!). It should return "The server is healthy!".
Be sure you have https:// in the base_url under the webserver section of your airflow config.
Digging into the gunicorn documentation: it seems to be possible to pass any command line argument (when gunicorn command is called) via the GUNICORN_CMD_ARGS environment variable.
So what I'm trying out is setting GUNICORN_CMD_ARGS=--forwarded-allow-ips=* since all the traffic will come to my instance from the AWS ALB... I guess the wildcard could be replaced with the actual IP of the ALB as seen by the instance, but that'll be next step...
Since I'm running on ECS, I'm passing it as:
- Name: GUNICORN_CMD_ARGS
Value: --forwarded-allow-ips=*
in the Environment of my task's container definition.
PS: from the doc, this possibility was added as of gunicorn 19.7; for comparison, Airflow 1.10.9 seems to be on gunicorn 19.10 so good to go with any (more or less) recent version of Airflow !
I encountered this issue too when using the official apache airflow helm chart (version 1.0.0).
Problem
Originally I had configured the webserver service with type LoadBalancer.
webserver:
service:
type: LoadBalancer
annotations:
service.beta.kubernetes.io/aws-load-balancer-internal: "true"
service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-west-2:1234512341234:certificate/231rc-r12c3h-1rch3-1rch3-rc1h3r-1r3ch
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp
This resulted in the creation of a classic elastic load balancer.
This mostly worked but when I clicked on the airflow logo (which links to https://my-domain.com), I'd get redirected to http://my-domain.com/home which failed because the load balancer was configured to use HTTPS only.
Solution
I resolved this by installing the AWS Load Balancer Controller on my EKS cluster and then configuring ingress.
The ingress-related portion of the chart config looks like this:
ingress:
enabled: true
web:
host: my-airflow-address.com
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/subnets: subnet-01234,subnet-01235,subnet-01236
alb.ingress.kubernetes.io/scheme: internal # if in private subnets
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
webserver:
service:
type: NodePort
Notes
It might be possible to configure the webserver to use an ALB instead of classic ELB and configure it to handle the HTTP routing, but I have not tested it.
I have a spring-boot application running on EC2 instance and it's publicly accessible from an elastic IP say 123.456.78.90 with the help of apache httpd server. I have given the following virtual host entry in httpd.conf
<VirtualHost *:80>
ProxyPreserveHost On
ProxyRequests Off
ServerName 123.456.78.90
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>
Now, I have installed Jenkins on the same EC2 instance and want it to be accessible from my elastic IP 123.456.78.90 but maybe by specifying a different port like 9090 so when I give 123.456.78.90:9090 it takes me to Jenkins but when I give 123.456.78.90 it takes me to my spring-boot application. I am not sure what is the best way to configure it. For setting up Jenkins I tried the following virtual host entry in my httpd.conf file but its not working.
<VirtualHost *:9090>
ProxyPreserveHost On
ProxyRequests Off
ServerName 123.456.78.90:9090
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>
I would appreciate if I am pointed in the right direction.
UPDATE: I have the simple rule for directing the inbound traffic over http
Why not just use the port directly in jenkins, i.e. 8080 instead of routing it through apache?
Anyways I think the problem is due to the lack of a listening directive in apache for port 9090
See https://httpd.apache.org/docs/2.4/bind.html
Have you tried to follow the manual on https://wiki.jenkins-ci.org/display/JENKINS/Installing+Jenkins+on+Ubuntu, the Setting up an Apache Proxy for port 80 -> 8080 section. I think just change the 80 from 9090 and then the manual might work for you.
Also if you are using EC2, you may have to do some security configuration about the port that can be accessed from outside network in you AWS console