I'm planning to deploy a Django site using Apache + mod_wsgi and PostgreSQL on Ubuntu 10.04.
I intend to connect to the database using IDENT authentication. For this I need to create a Postgresql user for Apache (www-data). I have chosen not to make this a superuser or provide any special privileges.
I have then created a database. I actually did this twice during testing. The first time I set the Apache user as the owner; the second time I set the owner as myself (superuser), and granted all privileges on the database to the Apache user.
When I use the Django syncdb management command (as myself), the tables created are not accessible to the Apache user. This can be resolved by granting all permissions to the Apache user for each table, but that's a bit of a nuisance.
The alternative seems to be allowing access as a superuser.
Is it considered safe/acceptable for my project to access a local db as a Postgresql superuser, and is it safe to use IDENT authentication? If not, what is the common practice?
EDIT: I've since found that switching PostgreSQL to use md5 authentication for local connections makes life easier.
When using ident authentication, connections to the database are via the Apache user during normal operation. When Django management commands are used, the connections are via the current user.
If you use MD5, both situations will connect to the database using the details specified in the DATABASES section of your settings.py file, avoiding the problems listed above.
I'm still interested to know if using a PostgreSQL superuser is wise.
Having applications connect as a superuser is almost definitely unwise. Unless the application needs to actually create and/or drop databases itself (and this is extremely unlikely), I don't think it's ever necessary. If the application connects to a database as that database's owner, it is effectively a superuser within the confines of that database, which might not be too bad.
I generally have applications access the database using an account authenticating with MD5. It's possible, for example, to set up pg_hba.conf such that the application account is the only account that can use MD5 authentication, and all other users on the local machine use ident/peer authentication.
It sounds like what you actually needed here was a role to group the Apache user and the other Django users together, so you could grant them access en masse.
Postgresql does have ways to grant permissions for all tables etc in a schema at once, and also a way to specify default permissions to be applied to new objects. This previous answer may be helpful: How do you create a read-only user in PostgreSQL?
IDENT authentication ended up being more hassle than it was worth. Here's what I ended up doing to avoid the use of a PostgreSQL superuser role...
Switch to the postgres linux user:
sudo su - postgres
Edit the PostgreSQL host-based authentication configuration file:
nano /etc/postgresql/8.4/main/pg_hba.conf
Scroll to near the bottom of this file, looking for the line which looks like this:
local all all ident
Change ident to md5, exit and save. This tells PostgreSQL to use an MD5-encrypted password for authentication on local connections. Now restart PostgreSQL:
/etc/init.d/postgresql-8.4 restart
Create a PostgreSQL user:
createuser django_user --pwprompt
Don't accept any of the special privileges when prompted. Now create a new database:
createdb -E UTF8 -O django_user django_db
Those options encode the database in UTF8 and set the owner to django_user. You can now exit back to the original linux user account:
exit
Your project settings file (settings.py) will need to include something like this:
DATABASES = {
'default': {
'ENGINE': 'django.contrib.gis.db.backends.postgis',
'NAME': 'django_db',
'USER': 'django_user',
'PASSWORD': '[your password]',
'HOST': '',
'PORT': '',
}
}
When you run python manage.py syncdb or any other Django management commands, the settings above will be used to authenticate with the database.
Related
I have a website that I built using Django and deployed on Heroku. The tutorial I followed had me set up a Heroku Postgres database attached as an add-on. After Heroku's recent pricing changes the Postgres database has gone from free to $5 / month.
Maybe static isn't the correct word, but the site doesn't have user accounts, collect user information, or otherwise have any need for a database. I believe the only information that's stored in the database is login info for the Django admin site. I was able to export a .pgdmp file from Heroku of the data stored in the database but couldn't make heads or tails of the contents.
Here's the settings.py file code for the database section:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
import dj_database_url
db_from_env = dj_database_url.config(conn_max_age=500)
DATABASES['default'].update(db_from_env)
My question is can I delete this add-on from the Heroku CLI or dashboard without breaking the site in production? Would I need to change anything in my settings.py file or elsewhere in my Django code first? I assume I would still be able to access the Django admin page on the development server, is that assumption correct?
Sorry for the possibly obvious question but I just don't want to break a site in production without doing some research first!
My question is can I delete this add-on from the Heroku CLI or dashboard without breaking the site in production?
We can't know for sure, but probably not.
You are using dj-database-url to set your default database. That likely means that your application is connecting to PostgreSQL via the DATABASE_URL environment variable.
SQLite does not work properly on Heroku due to its ephemeral filesystem. If you need a database, it needs to be a client-server database instead of a file-based database.
For anyone in my situation finding this in the future here's what I learned:
My initial hypothesis was correct, deleting the Postgres Heroku add-on left the site in production intact and working correctly. The only difference without the database is I can't access the Django admin page on the hosted site but I can still access this through the development server if needed.
Your site might be different than mine however, so your mileage may vary. Here's a couple things I did before making any changes to the site in production.
Check what tables/data you have in your database. Thanks to #Chris who answered in this thread and posted this link talking about the dataclips feature in Heroku. If you have tables that seem not to relate to the admin interface then you might be reliant on your database.
Consider spinning up another instance of your live site in production on Heroku. This way you can test and make changes without impacting users on the live site if something goes wrong.
I'm trying to restore the database with the maintenance script provided. But there is a check in the script which doesn't allow me to restore if the user is postgres.
Any reason for that ?
It is a custom to not use the postgres user in this case. Similar to the custom that when operating a linux server, you use a user account instead of the root account.
You can remove the passage from the script if you want to proceed anyway. However, cookiecutter-django should have generated a .env/.production/.postgres file with a different username than postgres.
I'm building a Django app which connects to a PostgreSQL database, and the credentials that the app uses has quite limited permissions granted over the relevant tables.
I'd like to be able to use south to manage database migrations, but given that this picks up on the same credentials that Django uses, this throws an error as south cannot make any changes to tables it doesn't own.
Is there a way to specify that south should use a different set of credentials to manage migrations other than that specified by the application settings?
Yes : have another set of settings with different credentials for south migrations and use the --settings option when calling the migrate command.
NB : to avoid DRY violation, you can as well start your special settings file by importing * from the normal settings and just override the DB credentials.
I'm taking EdX classes that use Ruby on Rails and python. That has given me courage to try and install and learn Django using Apach, mod_wsgi, and PostgreSQL. Following the detailed installation instructions, I first installed Apache, then mod_wsgi, and then PostgreSQL. I installed each from source and went through a little bit of tutorial with each and made sure they were properly installed. I've got a postgres user setup to run the PostgreSQL server and I was able to create a user "role" for myself as well as an admin role that my role inherits from that can create a database etc. I tried out some SQL in psql following a tutorial to make tables etc. I know how to grant privileges for a given role.
So anyway, I'm pretty close to the step where I would actually install Django from source, but I'm not sure how to follow this advice from the installation instructions:
If you plan to use Django's manage.py syncdb command to automatically create database tables for your models, you'll need to ensure that Django has permission to create and alter tables in the database you're using; if you plan to manually create the tables, you can simply grant Django SELECT, INSERT, UPDATE and DELETE permissions.
Maybe after I follow the steps to actually install Django and go through some tutorials, I'll understand exactly what needs to be setup in PostgreSQL to grant Django those permissions, but if I follow the installation instructions in order, it would seem to be saying I should setup those permissions now before installing Django. If I can get someone to tell me how to do it here before I do the install of Django, I'd appreciate it.
In the settings.py file of a django project, there is a snippet that says something like this:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'myproj_db',
'USER': 'myproj_user',
'PASSWORD': '123',
'HOST': '',
'PORT': '',
},
}
What this does is that it tells Django what user (postgres user in this case) and database is used in conjunction with your django project.
Normally, you will need to create this myproj_user together with the myproj_db.
When you create this user, you can choose to give it permissions like so:
CREATE USER myproj_user WITH PASSWORD '123' SUPERUSER CREATEDB CREATEROLE LOGIN;
This creates the myproj_user with superuser, createdbm createrole, login permissions allowed to the user.
And then the database like so:
CREATE DATABASE myproj_db WITH OWNER myproj_user TEMPLATE=template1 ENCODING='utf-8';
You say that you know how to grant privileges for a given role, which is what you need to do for Django before installing it (running syncdb).
This is part of setting up your database for use with Django – a step you take before creating each Django project. Each Django project corresponds to a site that you build with Django and is completely separate from another Django project. To each project belongs a database.* You can install Django the framework before you setup your database, because Django the framework doesn't do anything on its own.
Either you give Django permissions to create tables for you, in which case it can create tables for you (using manage.py syncdb). Or, you use manage.py sqlall <app> to get SQL that you run yourself to create the tables needed (which might be nice if you're paranoid about security).
To grant all permissions to a user for a specific database (option 1) in Postgres, use the command (from psql):
GRANT ALL PRIVILEGES ON DATABASE <db-name> TO <username>;
* Technically, they can share a database by simply configuring them to use the same one.
Django uses ORM (Object Relational Mapper). What that means is that you do not directly deal with database tables for querying things however you deal with certain classes which then deal with the database. This is very useful and much more user-friendly then doing manually SQL. Consider the following example:
SELECT * FROM foo WHERE field="value"
vs
Foo.objects.filter(field='value')
In ORM, you describe the tables you have by making certain classes (in Django we call them models) and those models correspond to tables in the db, and their fields correspond to the columns in the db table. The following are very similar:
CREATE TABLE foo (
title varchar(50),
description text,
likes integer
);
and
class Foo(models.Model):
title = models.CharField(max_length=50)
description = models.TextField()
likes = models.IntegerField()
However it is waste of time for you as a developer to construct the SQL statements for creating tables, and describing those tables in Python as models. Not to do that, Django allows you once you define all your models, to create db tables for you. That is the purpose of the syncdb command. It takes all the installed apps and models within them and creates tables within your database for them. However as you already mentioned in your question, databases have roles and those roles have permissions. For example, one of the permissions is CREATE as described here. So what Django documentation is saying is for you to grand all necessary permission to a role which Django will use to interact with the db for Django to be able to create necessary db tables it needs as well as possibly modify them later.
I've built an application that I want to move from my development server to my production server. In this application I have defined 3 custom groups in auth.group and each of those have specific permissions.
I've tried to dump the data from auth.group - it seems to include permissions ids as well. The problem is, those IDs don't match between my development environment and the production environment. It also seems there is a content_type_id in auth.permission that I don't know how it relates.
My question is, is there a way using dumpdata or something else, to migrate Groups and all of the related permissions for my application? I don't have a problem importing multiple fixtures on the production server, but I do want all of the groups to be set up without having to go through the UI and selecting the appropriate permissions for each group.
django.contrib.auth depends on django.contrib.contenttypes because auth.models.Permission.content_type is a ForeignKey(ContentType).
Solution: add ContentType in your data dump, ie. dumpdata with the following arguments: auth.group contenttypes.contenttype auth.permission