Jak mogę ominąć MySQL Errcode 13 za pomocą SELECT INTO OUTFILE?

114

Próbuję zrzucić zawartość tabeli do pliku CSV przy użyciu instrukcji MySQL SELECT INTO OUTFILE. Jeśli zrobię:

SELECT column1, column2
INTO OUTFILE 'outfile.csv'
FIELDS TERMINATED BY ','
FROM table_name;

Plik outfile.csv zostanie utworzony na serwerze w tym samym katalogu, w którym przechowywane są pliki tej bazy danych.

Jednak gdy zmieniam zapytanie na:

SELECT column1, column2
INTO OUTFILE '/data/outfile.csv'
FIELDS TERMINATED BY ','
FROM table_name;

Dostaję:

ERROR 1 (HY000): Can't create/write to file '/data/outfile.csv' (Errcode: 13)

Errcode 13 to błąd uprawnień, ale otrzymuję go, nawet jeśli zmienię własność / data na mysql: mysql i nadam mu 777 uprawnień. MySQL działa jako użytkownik „mysql”.

O dziwo mogę utworzyć plik w / tmp, ale nie w żadnym innym katalogu, którego próbowałem, nawet z uprawnieniami ustawionymi tak, że użytkownik mysql powinien mieć możliwość zapisu w katalogu.

To jest MySQL 5.0.75 działający na Ubuntu.

Ryan Olson
źródło
3
Biorąc pod uwagę, że 13 to błąd systemowy, prawdopodobnie to nie jest to, ale istnieje ustawienie MySQL ograniczające INTO OUTFILE do katalogu: dev.mysql.com/doc/refman/5.0/en/ ... może warto rzucić okiem, czy to ustawiony na /tmp.
Pekka
Ta zmienna jest pusta w mojej instalacji, co zgodnie z tym dokumentem oznacza, że ​​moje katalogi wyjściowe nie powinny być ograniczane.
Ryan Olson

Odpowiedzi:

189

Która konkretna wersja Ubuntu to jest i czy jest to wersja Ubuntu Server Edition?

Najnowsze wersje Ubuntu Server (takie jak 10.04) są dostarczane z AppArmor, a profil MySQL może być domyślnie w trybie wymuszania. Możesz to sprawdzić, wykonując w ten sudo aa-statussposób:

# sudo aa-status
5 profiles are loaded.
5 profiles are in enforce mode.
   /usr/lib/connman/scripts/dhclient-script
   /sbin/dhclient3
   /usr/sbin/tcpdump
   /usr/lib/NetworkManager/nm-dhcp-client.action
   /usr/sbin/mysqld
0 profiles are in complain mode.
1 processes have profiles defined.
1 processes are in enforce mode :
   /usr/sbin/mysqld (1089)
0 processes are in complain mode.

Jeśli mysqld jest włączony w tryb wymuszania, to prawdopodobnie on odmawia zapisu. Wpisy byłyby również zapisywane, /var/log/messagesgdy AppArmor blokuje zapis / dostęp. Możesz edytować /etc/apparmor.d/usr.sbin.mysqldi dodawać /data/i /data/*u dołu w następujący sposób:

...  
/usr/sbin/mysqld  {  
    ...  
    /var/log/mysql/ r,  
    /var/log/mysql/* rw,  
    /var/run/mysqld/mysqld.pid w,  
    /var/run/mysqld/mysqld.sock w,  
    **/data/ r,  
    /data/* rw,**  
}

A następnie spraw, aby AppArmor ponownie załadował profile.

# sudo /etc/init.d/apparmor reload

UWAGA: powyższa zmiana pozwoli MySQL na odczyt i zapis w katalogu / data. Mamy nadzieję, że już rozważyłeś konsekwencje tego dla bezpieczeństwa.

Vin-G
źródło
2
Nienawidzę tego wskazywać, ale jest powód, dla którego App Armor na to nie pozwala. MySQL ma teraz możliwość modyfikowania i odczytywania wszystkiego w folderze / data. Po prostu nie daj się teraz zhakować.
Ryan Ward
2
@Serdar, Zestaw reguł AppArmor MySQL dystrybuowany z dystrybucją domyślnie na to nie zezwala . Ma to sens, ponieważ jest to dobra podstawa reguł dla nowej instalacji. Uważam, że powinniśmy i wolno nam modyfikować zestawy reguł, aby pasowały do ​​naszych potrzeb po instalacji. Pierwotnym zamiarem pytającego było zezwolenie MySQL na zapis w określonych katalogach. Ale jeśli nie jest to jasno określone powyżej, uwaga dla kolejnych osób, które natkną się na to rozwiązanie: OSTRZEŻENIE: powyższa zmiana pozwoli MySQL na odczyt i zapis w katalogu / data. Mamy nadzieję, że już rozważyłeś konsekwencje tego dla bezpieczeństwa.
Vin-G,
1
ŚWIETNA ODPOWIEDŹ!!! To rozwiązało mój problem, próbowałem też pisać w innym katalogu. Teraz muszę się dowiedzieć, o co w tym wszystkim chodziło! :) Dowiedziawszy się o tym, polecam innym osobom przeczytanie o apparmor (a więc komendzie aa-status): en.wikipedia.org/wiki/AppArmor
David L
1
W moim przypadku pomogło to: /your/abs/folder/ r, /your/abs/folder/** rwk, }nie zapomnij dodać przecinka na końcu!
ACV
1
działa, aby pisać w / tmp. Zamiast tego użyj okien. Linux jest do bani
Victor Ionescu
17

Ubuntu używa AppArmor i właśnie to uniemożliwia dostęp do / danych /. Fedora używa selinux, a to zapobiegłoby temu na maszynie RHEL / Fedora / CentOS.

Aby zmodyfikować AppArmor, aby umożliwić MySQL dostęp / dane / wykonaj następujące czynności:

sudo gedit /etc/apparmor.d/usr.sbin.mysqld

dodaj tę linię w dowolnym miejscu listy katalogów:

/data/ rw,

następnie wykonaj:

sudo /etc/init.d/apparmor restart

Inną opcją jest całkowite wyłączenie AppArmor dla mysql, NIE jest to ZALECANE :

sudo mv /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/

Nie zapomnij ponownie uruchomić apparmor:

sudo /etc/init.d/apparmor restart

wieża
źródło
Aby faktycznie wyłączyć apparmor dla mysql, musiałem zrobić: cyberciti.biz/faq/ubuntu-linux-howto-disable-apparmor-commands
silver_mx
14

Wiem, że powiedziałeś, że próbowałeś już ustawić uprawnienia na 777, ale ponieważ mam dowody, że dla mnie był to problem z uprawnieniami, publikuję to, co dokładnie uruchamiam, mając nadzieję, że może to pomóc. Oto moje doświadczenie:

tmp $ pwd
/Users/username/tmp
tmp $ mkdir bkptest
tmp $ mysqldump -u root -T bkptest bkptest
mysqldump: Got error: 1: Can't create/write to file '/Users/username/tmp/bkptest/people.txt' (Errcode: 13) when executing 'SELECT INTO OUTFILE'
tmp $ chmod a+rwx bkptest/
tmp $ mysqldump -u root -T bkptest bkptest
tmp $ ls bkptest/
people.sql  people.txt
tmp $ 
basilikode
źródło
me @ server: / data $ pwd / data me @ server: / data $ ls -al total 60 ... drwxrwxrwx 2 mysql mysql 4096 2010-05-06 16:27 dumptest me @ server: / data $ mysqldump -u dbuser -p -T dumptest -B nazwa_db --tables test Wprowadź hasło: mysqldump: Otrzymałem błąd: 1: Nie można utworzyć / zapisać do pliku '/data/dumptest/test.txt' (Errcode: 13) podczas wykonywania polecenia 'SELECT INTO PLIK WYJŚCIOWY 'me @ server: / data $ sudo chmod a + rwx dumptest / me @ server: / data $ mysqldump -u dbuser -p -T dumptest -B nazwa_db --tables test Wprowadź hasło: mysqldump: Otrzymano błąd: 1: ( ten sam błąd)
Ryan Olson
No cóż, nie zdawałem sobie sprawy, że komentarze nie zostaną sformatowane, ale dwukrotnie sprawdziłem kilka różnych sposobów. Najpierw z katalogiem docelowym należącym do mysql: mysql, a następnie z katalogiem docelowym należącym do użytkownika, w którym wykonałem polecenie dump, ponieważ oba sposoby nadal dają mi ten sam błąd uprawnień.
Ryan Olson
Dla przypomnienia, zadziałało to dla mnie pomimo zmiany uprawnień aplikacji
Alex
Próbowałem dokonać modyfikacji w apparmor, ale to nie zadziałało. Zmiana uprawnienia „chmod 777” zadziałała dla mnie!
Sudarshan_SMD
7

MySQL robi się tu głupi. Próbuje utworzyć pliki w katalogu / tmp / data / .... Więc co możesz zrobić, to:

mkdir /tmp/data
mount --bind /data /tmp/data

Następnie spróbuj zapytać. To zadziałało po wielu godzinach debugowania problemu.

vimdude
źródło
Najbardziej podoba mi się ta odpowiedź. To jest łatwe, działa i nie wymaga od ciebie fatygowania się z apparmor. Drugi sposób korzystania z potoków nie działa dobrze w przypadku dużych eksportów ze względu na całe buforowanie, które jest wykonywane.
Chris Seline
6

Ten problem trapi mnie od dawna. Zauważyłem, że ta dyskusja nie wskazuje rozwiązania na RHEL / Fecora. Używam RHEL i nie znajduję plików konfiguracyjnych odpowiadających AppArmerowi na Ubuntu, ale rozwiązałem swój problem, czyniąc KAŻDY katalog w katalogu PATH czytelnym i dostępnym dla mysql. Na przykład, jeśli utworzysz katalog / tmp, następujące dwie komendy sprawią, że SELECT INTO OUTFILE będzie w stanie wyprowadzić plik .sql AND .sql

chown mysql:mysql /tmp
chmod a+rx /tmp

Jeśli tworzysz katalog w swoim katalogu domowym / home / tom, musisz to zrobić zarówno dla / home, jak i / home / tom.

fanchyna
źródło
3
Użycie / tmp jako przykładu nie jest dobrym pomysłem i naprawdę nie chcesz zmieniać prawa własności do katalogu / tmp (w większości przypadków).
sastorsl
Zmiana właściciela / tmp jest zła, ale utworzenie folderu tymczasowego w / tmp i chown mysql:mysqlrozwiązanie mojego problemu
Samuel Prevost
6

Możesz to zrobić :

mysql -u USERNAME --password=PASSWORD --database=DATABASE --execute='SELECT `FIELD`, `FIELD` FROM `TABLE` LIMIT 0, 10000 ' -X > file.xml
Navrattan Yadav
źródło
Dzięki! Czy można sterować wyjściem jako CSV?
Hamman Samuel
4

Kilka rzeczy do wypróbowania:

  • czy secure_file_privzmienna systemowa jest ustawiona? Jeśli tak, wszystkie pliki muszą zostać zapisane w tym katalogu.
  • upewnij się, że plik nie istnieje - MySQL utworzy tylko nowe pliki, nie nadpisze istniejących.
mdma
źródło
1
Wybrałbym również secure_file_priv. Jeśli plik już istnieje, komunikat o błędzie jest inny (nie kod błędu 13).
Xavier Maillard
Secure_file_priv nie jest obecnie ustawiony, więc jak rozumiem, oznacza to, że nie powinienem ograniczać się do tego, gdzie mogę zapisywać pliki. Czy źle to rozumiem i czy muszę wyraźnie ustawić to na coś takiego jak „/”, jeśli chcę mieć możliwość zapisu w dowolnym miejscu w systemie plików?
Ryan Olson
Przed uruchomieniem zapytania sprawdzam również, czy plik nie istnieje.
Ryan Olson
Dziękujemy za opinię. Opierając się na twoich ustaleniach, nie sądzę, aby żadna z tych sugestii spowodowała twój problem.
mdma
3

Mam ten sam problem i rozwiązałem go, wykonując następujące czynności:

  • System operacyjny: ubuntu 12.04
  • lampa zainstalowana
  • załóżmy, że katalog do zapisania pliku wyjściowego to: / var / www / csv /

Wykonaj następujące polecenie na terminalu i edytuj ten plik za pomocą edytora gedit, aby dodać katalog do pliku wyjściowego.

sudo gedit /etc/apparmor.d/usr.sbin.mysqld

  • teraz plik zostałby otwarty w edytorze, dodaj tam swój katalog

    / var / www / csv / * rw,

  • podobnie dodałem w moim pliku, jak podany obraz:

wprowadź opis obrazu tutaj

Wykonaj następne polecenie, aby ponownie uruchomić usługi:

sudo /etc/init.d/apparmor restart

Na przykład wykonuję następujące zapytanie do kreatora zapytań phpmyadmin, aby wyprowadzić dane w pliku csv

SELECT colName1, colName2,colName3
INTO OUTFILE '/var/www/csv/OUTFILE.csv'
FIELDS TERMINATED BY ','
FROM tableName;

Udało się i zapisał wszystkie wiersze z wybranymi kolumnami do pliku OUTPUT.csv ...

Sunny SM
źródło
2

W moim przypadku rozwiązaniem było uczynienie każdego katalogu w ścieżce katalogu czytelnym i dostępnym przez mysql( chmod a+rx). Katalog był nadal określony przez swoją ścieżkę względną w wierszu poleceń.

chmod a+rx /tmp
chmod a+rx /tmp/migration
etc.
Alsciende
źródło
2

Właśnie napotkałem ten sam problem. Mój problem polegał na tym, że katalog, do którego próbowałem zrzucić, nie miał uprawnień do zapisu dla procesu mysqld. Początkowy zrzut sql zostałby zapisany, ale zapis pliku csv / txt nie powiódł się. Wygląda na to, że zrzut sql działa jako bieżący użytkownik, a konwersja do csv / txt jest uruchamiana jako użytkownik, który uruchamia mysqld. Dlatego katalog wymaga uprawnień do zapisu dla obu użytkowników.

Matthew McMillan
źródło
1

Musisz podać ścieżkę bezwzględną, a nie ścieżkę względną.

Podaj pełną ścieżkę do katalogu / data, w którym próbujesz pisać.

Ike Walker
źródło
Dla mnie to wygląda na absolutną ścieżkę. Czyż nie?
Pekka
2
Spróbuj tego jako użytkownik mysql, aby sprawdzić, czy możesz utworzyć plik poza mysql:touch /data/outfile.csv
Ike Walker
1
Po pierwsze nie mogłem tego zrobić, ponieważ powłoka użytkownika mysql była ustawiona na / bin / false, więc nie mogłem zalogować się jako mysql. Aby upewnić się, że to nie przyczynia się do problemu, ustawiłem powłokę mysql na / bin / bash, su'd dla tego użytkownika i dotknąłem pliku w / data. Plik został pomyślnie utworzony, którego właścicielem jest mysql.
Ryan Olson
3
Możesz zalogować się na konto, nawet jeśli używa jednej z "wyłączonych" powłok: su --shell=/bin/sh nameofaccount
Marc B
Dzięki, nie byłem tego świadomy.
Ryan Olson
1

Czy Ubuntu używa SELinux? Sprawdź, czy jest włączona i wymusza. /var/log/audit/audit.log może być helpul (jeśli to jest miejsce, w którym Ubuntu go umieszcza - to jest lokalizacja RHEL / Fedora).

Charles
źródło
0

Miałem ten sam problem na CentOs 6.7 W moim przypadku wszystkie uprawnienia były ustawione i nadal występował błąd. Problem polegał na tym, że SE Linux był w trybie „wymuszania”.

Zmieniłem to na „przyzwolenie” za pomocą polecenia sudo setenforce 0

Wtedy wszystko mi się udało.

Stefan Bicher
źródło