AWS Beanstalk couldn't create nginx config file at the deployment automatically - amazon-web-services

I have created a Django API application that is deployed on AWS Beanstalk Amazon Linux 2 (Python 3.8). In a part of my application, the user should be able to upload a file through API.
Nginx, on default, is returning CORS error to the user when the uploaded file is bigger than 3 MB and logging 'user tries to upload huge file' in the Nginx log.
The only solution which works for me is creating the bellow configuration file and reloading the Nginx:
/etc/nginx/conf.d/proxy.conf:
client_max_body_size 50M;
and then:
sudo service nginx reload
I have done this procedure manually by connecting to the EC2 which is the host of my beanstalk application via SSH. I want to automate this procedure to be done at every deployment and every instance.
I have created a file called nginx_max_upload.config file in .ebextensions folder which is located in the root of my project:
nginx_max_upload.config:
files:
/etc/nginx/conf.d/proxy.conf:
mode: "000755"
owner: root
group: root
content: |
client_max_body_size 50M
commands:
reload_nginx:
command: "sudo service nginx reload"
ignoreErrors: true
My problem is, the above code didn't create the file I want in the specified directory. When I changed the directory to another directory like /usr/local/bin/proxy.conf, the file would be successfully created but it can't create the config file in the Nginx configuration folder.
I guess the problem might be from the permissions but I don't know how to grant the needed permission to the deployment agent.
Also, I have tried these two solutions but none of them works:
I have tried to create the config file in another folder and then, move it via mv command to the right directory but it didn't work.
Also, I have tried to put the creation code in the predeply hook and put manual echo commands in my code. I saw all of my echo commands output in the beanstalk logs but it didn't do anything (nor creating the file in the nginx configuration neither moving it from somewhere else to the configuration folder).

Since you are using Amazon Linux 2 (AL2), your configuration files are incorrect. They used to work in AL1, but for AL2, they are in different place and have different format as shown in the docs.
Thus could have the following .platform/nginx/conf.d/myconfig.conf (not in .ebextensions) with content:
client_max_body_size 50M;

Related

Can't create file elastic beanstalk

I run a django app on eb. My goal is to create a cron script in /etc/cron.d at deployment time that runs django jobs. I tried to create it using a an eb configuration file, but so far I've been unable to create any file anywhere (manually checked by sshing into the host).
Platform: Python 3.7 running on 64bit Amazon Linux 2/3.3.9
The content of cron.config looks like this:
files:
"/etc/cron.d/cron_job":
mode: "000644"
owner: root
group: root
content: |
* * * * * root echo "Cron running at "`date` > /home/ec2-user/cron_job.log
What I've tried is following. In all cases, app deploys but no file created.
Placing cron.config in .ebextensions folder.
Placing cron.config in .platform/hook/postdeploy folder. Log says "The dir .platform/hooks/postdeploy/ does not exist in the application. Skipping this step...".
Specify file location as /tmp/cron_job.
Specify file location as /home/ec2-user/cron_job.
Would appreciate any help.
There is nothing wrong with your cron.config and it creates /etc/cron.d/cron_job. It works as expected as I run it on my own EB application. Whatever is happening is due to some other factors/code which is not shown in your question.

Elastic BeanStalk app deploy post hook not executing my command

I recently was able to get my Laravel app deployed using codepipeline on Elastic Beanstalk but ran into a problem. I noticed that my routes where failing because of php.conf Nginx configuration. I had to add a few lines of code to EB's nginx php.conf file to get it to work.
My problem now was that after every deployment, the instance of the application I modified the php.conf file was destroyed and recreated fresh. I wanted a way to dynamically update the file after every successful deployment. I had a version of the file I wanted versioned with my application and so wanted to create a symlink to that file after deployment.
After loads of research, I stumbled on appDeploy Hooks on Elastic Beanstalk that runs post scripts after deployment so did this
files:
"/opt/elasticbeanstalk/hooks/appdeploy/post/91_post_deploy_script.sh":
mode: "000755"
owner: root
group: root
content: |
#!/usr/bin/env bash
sudo mkdir /var/testing1
sudo ln -sfn /var/www/html/php.conf.example /etc/nginx/conf.d/elasticbeanstalk/php.conf
sudo mkdir /var/testing
sudo nginx -s reload
And this for some reason does not work. The symlink is not created so my routes are still not working..
I even added some mkdir so am sure the commands in that script runs, none of those commands ran because none of those directories where created.
Please note that if I ssh into the ec2 instance and run the commands there it works. That bash script also exists in the post directory and if I manually run in on the server it works too.
Any pointers to how I could fix this would be helpful. Maybe I am doing something wrong too.
Now I have gotten my scripts to run by following this. However, the script is not running. I am getting an error
2020/06/28 08:22:13.653339 [INFO] Following platform hooks will be executed in order: [01_myconf.config]
2020/06/28 08:22:13.653344 [INFO] Running platform hook: .platform/hooks/postdeploy/01_myconf.config
2020/06/28 08:22:13.653516 [ERROR] An error occurred during execution of command [app-deploy] - [RunPostDeployHooks]. Stop running the command. Error: Command .platform/hooks/postdeploy/01_myconf.config failed with error fork/exec .platform/hooks/postdeploy/01_myconf.config: permission denied
I tried to follow this forum post here to make my file executable by adding to my container command a new command like so:
01_chmod1:
command: "chmod +x .platform/hooks/postdeploy/91_post_deploy_script.sh"
I am still running into the same issue. Permission denied
Sadly, the hooks you are describing (i.e. /opt/elasticbeanstalk/hooks/appdeploy) are for Amazon Linux 1.
Since you are using Amazon Linux 2, as clarified in the comments, the hooks you are trying to use do not apply. Thus they are not being executed.
In Amazon Linux 2, there are new hooks as described here and they are:
prebuild – Files here run after the Elastic Beanstalk platform engine downloads and extracts the application source bundle, and before it sets up and configures the application and web server.
predeploy – Files here run after the Elastic Beanstalk platform engine sets up and configures the application and web server, and before it deploys them to their final runtime location.
postdeploy – Files here run after the Elastic Beanstalk platform engine deploys the application and proxy server.
The use of these new hooks is different than in Amazon Linux 1. Thus you have to either move back to Amazon Linux 1 or migrate your application to Amazon Linux 2.
General migration steps from Amazon Linux 1 to Amazon Linux 2 in EB are described here
Create a folder called .platform in your project root folder and create a file with name 00_myconf.config inside the .platform folder.
.platform/
00_myconf.config
Open 00_myconf.config and add the scripts
files:
"/opt/elasticbeanstalk/hooks/appdeploy/post/91_post_deploy_script.sh":
mode: "000755"
owner: root
group: root
content: |
#!/usr/bin/env bash
sudo mkdir /var/testing1
sudo ln -sfn /var/www/html/php.conf.example /etc/nginx/conf.d/elasticbeanstalk/php.conf
sudo mkdir /var/testing
sudo nginx -s reload
Commit your changes or reupload the project. This .platform folder will be considered in each new instance creation and your application will deploy properly in all the new instances Amazon Elastic beanstalk creates.
If you access the documentation here and scroll to the section with the title "Application example with extensions" you can see an example of the folder structure of your .platform folder so it adds your custom configuration to NGINX conf on every deploy.
You can either replace the entire nginx.conf file with your file or add additional configuration files to the conf.d directory
Replace conf file with your file on app deploy:
.platform/nginx/nginx.conf
Add configuration files to nginx.conf:
.platform/nginx/conf.d/custom.conf

Access Elastic Beanstalk environment properties in NGINX configs running on AWS Linux 2

I had this working before on AWS Linux AMI but no luck with AWS Linux 2.
I need to access my environment properties from the Nginx configuration file during the EB application deployment. It's a Single instance Node Server.
I did it like this with the AWS Linux AMI and it worked without a problem:
.ebextensions/00_options.config
option_settings:
aws:elasticbeanstalk:application:environment:
DOMAIN: socket.example.com
MASTER_DOMAIN: https://example.com
etc..
.ebextensions/10_proxy.config
... some configs ...
files:
/etc/nginx/conf.d/proxy.conf:
mode: "000644"
owner: root
group: root
content: |
upstream nodejs {
server 127.0.0.1:8081;
keepalive 256;
}
map $http_origin $cors_header {
hostnames;
default "";
`{"Fn::GetOptionSetting": {"Namespace": "aws:elasticbeanstalk:application:environment", "OptionName": "MASTER_DOMAIN"}}` "$http_origin";
}
server {
listen 80;
listen 8080;
server_name `{"Fn::GetOptionSetting": {"Namespace": "aws:elasticbeanstalk:application:environment", "OptionName": "DOMAIN"}}`;
location ~ /.well-known {
allow all;
root /usr/share/nginx/html;
}
location / {
return 301 https://$host$request_uri;
}
}
etc..
.... some more configs ....
I'm not including most of the configs above because they're not relevant.
So when I did this before, everything worked as expected. The config file inserted my properties and created the file in the /etc/nginx/conf.d/proxy.conf folder.
Now with AWS Linux 2 the specs have changed and we have to add our Nginx configuration files in the .platform/nginx/conf.d folder located in our application bundle root folder.
Here the reference ( see Reverse proxy configuration)
So I created a proxy.conf file in the location mentioned above with the content that was previously inserted in /etc/nginx/conf.d/proxy.conf.
.platform/nginx/conf.d/proxy.conf
upstream nodejs {
server 127.0.0.1:8081;
keepalive 256;
}
map $http_origin $cors_header {
hostnames;
default "";
`{"Fn::GetOptionSetting": {"Namespace": "aws:elasticbeanstalk:application:environment", "OptionName": "MASTER_DOMAIN"}}` "$http_origin";
}
etc...
And then the problems began..
This first trial throwed unexpected "{" in /var/proxy/staging/nginx/conf.d/proxy.conf:11 at me.
And after that I tried a lot of things. Tried it with ${MASTER_DOMAIN} and messed around with the new EB AWS Linux 2 hooks (see link above Platform hooks). All for no avail it seems like you can't access the properties from the Nginx configs. I've read an article or a documentation from Nginx mentioning something similar today but I can't find it anymore (did a lot of googling).
I also tried to create a config file like I did with the working version which purpose was to save a temp file somewhere with the included properties and then include this file in the needed .platform/nginx/conf.d/proxy.conf file because I started to think that there is no way to include them directly with the new specs.
.ebextensions/10_proxy.config
... some configs ....
files:
/var/proxy/staging/custom_folder/proxy.conf:
mode: "000644"
owner: root
group: root
content: |
etc...
.platform/nginx/conf.d/proxy.conf
include custom_folder/proxy.conf;
With this idea in mind I did a lot of nonsense, I created hooks for creating (mkdir) directories in which I tried to temporarily save the file which leaded to new permission errors. I wasn't able to give the proper permissions to prebuild, postdeploy files but this is another issue.
And a lot more of trying and failing...
But then I've read (also from the link above):
"If you configure your proxy to send traffic to multiple application processes, you can configure several environment properties, and use their values in both proxy configuration and your application code."
And hope came back.. Does this mean I actually CAN directly add environmental variables into the Nginx configs located in the .platform directory? ... I don't know.. Do you?
I could continue to describe all the things I tried all night long so I will stop here. I hope you get the issue. If not ask me and I will do my best to make all this understandable.
Also my mind isn't very clear anymore after 14 hours of battling this issue. I need a break.
If you did it to the end thank you for your time and help would be greatly appreciated.
Summary
One way to do it is to create a shell script in .platform/hooks/postdeploy.
Here is a simplified example, assuming you have an Elastic Beanstalk environment property called MASTER_DOMAIN:
#!/bin/bash
# write nginx config file
cat > /etc/nginx/conf.d/elasticbeanstalk/test.conf << LIMIT_STRING
location /test/ {
default_type text/html;
return 200 "nginx variable: \$host, and EB env property: $MASTER_DOMAIN";
}
LIMIT_STRING
# restart nginx service so the config takes effect
systemctl restart nginx.service
The location block from this example can be replaced by the nginx content from .ebextensions/10_proxy.config in the original post. No need for the Fn::GetOptionSetting stuff though.
I think you also need a duplicate script in .platform/confighooks/postdeploy.
Details below.
(sorry for the wall of text)
Environment variables in nginx
Actually, as discussed in here and here, it is not possible (out-of-the-box) to use os environment variables inside the http, server, or location blocks in nginx config files. There are some workarounds, such as using lua, perl, or templates, but let's not get into those. This part has nothing to do with AWS.
In the OP's original configuration for Amazon Linux AMI (AL1), using the files section in .ebextensions/10_proxy.config, they were actually using a shell script to write the nginx config file during deployment. The shell script expanded the environment variables, but the resulting proxy.conf for nginx did not actually access any environment variables.
That's why it worked on AL1.
Platform hooks
Now, for Amazon Linux 2 (AL2), we can do something similar using shell scripts in the .platform/hooks and .platform/confighooks folders.
These .platform hook scripts are executed as the root user, and they have access to the Elastic Beanstalk (EB) environment properties. The EB environment properties can be accessed just like normal OS environment variables, so there is no need to use the Fn::GetOptionSetting stuff.
Basically, we need to create a shell script that writes a file with the content from your original .ebextensions/10_proxy.config. However, there are two questions we need to consider:
Should we use a prebuild, predeploy, or postdeploy hook?
What is the proper destination directory for our nginx proxy.conf file?
File locations
To answer these questions, we have to refer to the AWS documentation for Extending Elastic Beanstalk Linux platforms, and specifically the Instance deployment workflow section.
... The current working directory (cwd) for platform hooks is the application's root directory. For prebuild and predeploy files it's the application staging directory, and for postdeploy files it's the current application directory. If one of the files fails (exits with a non-zero exit code), the deployment aborts and fails.
This is interesting, but leaves some questions, e.g. where is the "application staging directory" located? We can fill in the blanks by inspecting one of our deployment log files. Based on our eb-engine.log, here's what happens with the platform hooks and nginx config files during app deployment (skipping a lot of details):
the source bundle is downloaded from S3 and extracted to /var/app/staging/
platform hooks in .platform/hooks/prebuild/ are executed
proxy server configuration is copied from /var/app/staging/.platform/nginx/ to /var/proxy/staging/nginx
platform hooks in .platform/hooks/predeploy/ are executed
proxy server is started, configuration is copied from /var/proxy/staging/nginx/ to /etc/nginx
platform hooks in .platform/hooks/postdeploy/ are executed
Note, after deployment the app is located in /var/app/current.
Based on the above, there are several options:
Create a shell script in .platform/hooks/postdeploy that writes to /etc/nginx/conf.d/proxy.conf.
The nginx service is already running, at this stage, so we need to restart for the configuration to take effect.
Below is a minimal test example. In this example we write to the elasticbeanstalk subdirectory, because we just want to add a location inside the default server block. We can then visit the /test/ page in a browser, to check that the configuration works.
We use some bash io redirection (<<, >) to write the nginx config file.
Note that we need to escape any nginx variables, e.g. $host becomes \$host, otherwise the shell will interpret them as environment variables.
Also note that the shell scripts need to have execution permission, as explained under More about platform hooks in the docs.
#!/bin/bash
cat > /etc/nginx/conf.d/elasticbeanstalk/test.conf << LIMIT_STRING
location /test/ {
default_type text/html
return 200 "nginx variable: \$host, and EB env property: $MASTER_DOMAIN";
}
LIMIT_STRING
systemctl restart nginx.service
Alternatively, we could create a shell script in .platform/hooks/predeploy that writes to /var/proxy/staging/nginx/conf.d/proxy.conf.
There is no need to restart the nginx service in this case, because this hook is executed before the server configuration is applied.
BEWARE:
Not sure if this is a bug or a design feature, but our newly created proxy.conf disappears after a configuration deployment (as opposed to an application deployment), unless we put a duplicate script in the .platform/confighooks/postdeploy directory. Not very DRY...
EDIT: AWS support confirmed that we need duplicate scripts in hooks and confighooks in this case. The application example in the docs also shows some duplicates (at least duplicate filenames) in hooks and confighooks.
EDIT:
Instead of duplicating scripts, we can also write a confighook that calls a hook, e.g. .platform/confighooks/predeploy/01_my_confighook.sh could look like this:
#!/bin/bash
source "/var/app/current/.platform/hooks/predeploy/01_my_hook.sh"
Disclaimer: This was tested on a freshly created single instance EB environment with "Python 3.7 running on 64bit Amazon Linux 2/3.1.5" using all default configuration and the default AWS Python sample application (only extended with our custom hooks).

How to create folder on Elastic Beanstalk server to install LetsEncrypt SSL certificate with AcmePHP

I have a site running on an Elastic Beanstalk single instance server and want to add automated SSL certificate generation from LetsEncrypt using the AcmePHP library.
The library tries to store the certificates in ~/.acmephp, which the server responds to with an error
Failed to create "/home/webapp/.acmephp": mkdir(): Permission denied.
The acmephp library doesn't have an option to change the path built in, and rather than fork and recompile the script, I'd like to be able to store the files in the default directory.
Does anyone know how I can give the app permission to create this directory, outside of the web root, or how I can make the server create it automatically and have it be available to the app?
It looks like since it's being ran by the webapp user, when acmePHP is trying to store the certificate under that user's home directory it fails because that directory doesn't exist (afaik the webapp user only runs httpd and it definitely doesn't have a home directory).
A very dirty workaround could be manually creating that file and folder in the . ebextensions folder in your project.The file would be .ebextensions/create_home.config and it would contain something like this:
files:
"/tmp/create-home.sh" :
mode: "000755"
content: |
#!/usr/bin/env bash
mkdir -p /home/webapp
chown webapp:webapp -R /home/webapp
commands:
01_create:
command: "/tmp/create-home.sh"
That script is ran by the root user, and afterwards it changes ownership of the /home/webapp folder to the webapp user and group respectively. Hope it helps

AWS Elastic Beanstalk - EB Extensions Not Working

I've done this before a long time ago, but now it's not working... :)
I am trying to use EBExtensions in an ElasticBeanstalk application. I created a vanilla Elastic Beanstalk environment with no configuration beyond the defaults. I gave it an application version that had a directory structure like the following:
.ebextensions
40testextension.config
app.js
other files
The important part is that I have a folder called .ebextensions at the root of my deployable artifact, which is where I believe it should be located.
The 40testextension.config file inside that file has the following contents:
files:
"/home/ec2-user/myfile" :
mode: "000755"
owner: root
group: root
content: |
# This is my file
# with content
I uploaded that version when creating the environment, and the environment created successfully. But when I look for that file, it is not present. Furthermore, when do a recursive grep for that ebextension file name in the logs at /var/log, I only get one result:
./eb-activity.log: inflating: /tmp/deployment/application/.ebextensions/40testextension.config
Having looked at the logs, it seems that the file is present when the artifact gets pulled down to the host, but the ebextension never gives any indication of running.
What am I missing here? I've done this in the distant past and things have worked very nicely, but this time I can't seem to get the thing to be executed by the Beanstalk deploy lifecycle.
try to run it with -x Print commands and their arguments as they are executed to debug and try to change the mode to 000777.
files:
"/home/ec2-user/myfile" :
mode: "000777"
owner: root
group: root
content: |
#!/usr/bin/env bash
set -xe