Maven Elastic Beanstalk Plugin

Hiljattain kirjoittelin Spring Boot projektista, jolla on helppo saada aikaan web sovellus joka tukee Java EE teknologioita mutta pyörii jopa Tomcat serverillä. Yksi hyöty mikä tästä saavutetaan on helppo mahdollisuus tuikata paketti vaikka Elastic Beanstalk-pilveen.

Tässä on Maven plug-in joka hoitaa homman (lisää build-osioon pom.xml:ssä):

<plugin>
 <groupId>br.com.ingenieux</groupId>
 <artifactId>beanstalk-maven-plugin</artifactId>
 <version>1.3.1</version>
 <configuration>
 <s3Bucket>elasticbeanstalk-eu-west-1-11111111111</s3Bucket>
 <s3Key>${project.artifactId}/${project.build.finalName}-${maven.build.timestamp}.war</s3Key>
 <region>eu-west-1</region>
 <!-- will save as s3://[bucket name]/[artifactId]/artifactId-version-TIMESTAMP.war -->
 <autoCreateApplication>true</autoCreateApplication>
 <applicationName>${project.artifactId}App</applicationName>
 <environmentName>${project.artifactId}Env</environmentName>
 <cnamePrefix>${project.artifactId}app</cnamePrefix>
 <versionLabel>${project.version}-${buildNumber}</versionLabel>
 </configuration>
 </plugin>

Luonnollisesti s3 bucket id:t ja regionit kuntoon tuossa. Ja näin aukeaa uusia komentoja:

mvn beanstalk:upload-source-bundle beanstalk:create-application-version beanstalk:create-environment

Ylläoleva luo beanstalkin avulla Amazon AWS klusteriin uuden web serverin pyörittämään softaa.

Tämä taas päivittää softan uuteen versioon, mukava yhdistää esim. mvn clean install käskyihin:

mvn beanstalk:upload-source-bundle beanstalk:create-application-version beanstalk:update-environment

Huom! Nämä kaverit kaipaavat myös autentikointia, se taas onnistuu kun tekee settings.xml tiedostoon .m2 kansioon tällaista:

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
 http://maven.apache.org/xsd/settings-1.0.0.xsd">
   <servers>
     <server>
     <id>aws.amazon.com</id>
       <username>AATAJABEETÄJACEETÄ</username>
       <password>HÄRPÄTIHÄRPÄTI</password>
     </server>
   </servers>
 </settings>

Nämä tiedot tietysti löytyvät Amazon AWS alta, ja luonnollisesti salasanasta on parasta käyttää kryptattua muotoa, mitä Maven tukeekin mukavasti. Huom! Tietysti myös ylläolevassa plugin-asetuksessa syytä asettää tuo s3 bucket id oikein.

Lisäksi, jos lisäät tämän plugin-osion configurationin jälkeen, deploy tapahtuu automaattisesti esim. mvn clean install komennolla:

<executions>
  <execution>
    <phase>install</phase>
    <goals>
      <goal>upload-source-bundle</goal>
      <goal>create-application-version</goal>
      <goal>update-environment</goal>
    </goals>
  </execution>
</executions>

No niin, tuossa pluginissa on kuitenkin metka bugi, ainakin nykyversiossa. Jostain syystä create-vaiheessa se vaatii cnamePrefix:in parametrina (ja luo tomcat ympäristön joka ei tue edes Java 7:aa vaan on Java 6 tasossa – kamalaa! – mutta ei vielä se bugi) – mutta update vaiheessa taas tuo cnamePrefix kylmästi ohitetaan ja sen sijaan on jotenkin kovakoodattu artifactId:n käyttö.

Toisin sanoen, fiksuinta on tehdä beanstalkkiin ensin sopiva projekti jossa on oikeasti Java 7 tuki (ja saisi se 8:kin tippua sinne jossain vaiheessa..) – ja pitää huolta että cnamePrefix ja projektin artifactId ovat prikulleen, case-sensitiivisesti samat. Silloin piisaa konfiguraatioksi:

<plugin>
  <groupId>br.com.ingenieux</groupId>
  <artifactId>beanstalk-maven-plugin</artifactId>
  <version>1.3.1</version>
  <configuration>
    <applicationName>NewFacebookApp</applicationName>
     <s3Key>${project.artifactId}/${project.build.finalName}-${maven.build.timestamp}.war</s3Key>
     <s3Bucket>elasticbeanstalk-eu-west-1-1112223333</s3Bucket>
     <region>eu-west-1</region>
     <environmentName>NewFacebookEnvironment</environmentName>
  </configuration> 
</plugin>

Ja tarkista siis, että projektin alussa määritelty artifactId = beanstalkissa oleva url prefix osa, eli se mitä on ennen elasticbeanstalk.com osaa. Sitten toimii.

Eli ei ihan vakaata kamaa tämä plugin. Mutta toimiessaan ihmeellisen ihana.

(kirosanat editoitu pois konfiguraation kommenteista)

 

 

Mainokset

Ei ole isoisäsi Spring myöskään enää!

Jep, Spring Framework on tosiaan uudistunut rajusti. Ajattelin jatkaa sepustusta siitä mitä Spring Boot projektilla voi tehdä. Tein jo aiemmin perus web projektin jossa on RESTful web service. Tällä kertaa sukelletaan Spring data pakettiin.

Lisää projektiisi riippuvuudet:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
  <groupId>org.hsqldb</groupId>
  <artifactId>hsqldb</artifactId>
  <scope>runtime</scope>
</dependency>

Jep, tässä tuli siis Hibernate, Spring Data JPA, ja Spring ORMS paketit mukana. Ja testikannaksi embedded hsqldb – voi tietysti olla myös derby, tai ihan tuotantokantoja tms. Mutta jostan hyvä aloitella.

Lisätään Entity Object. Spring ei vaadi persistence.xml tiedostoa, vaan oletuksena kaikki paketit pääkonfiguraatiotiedostos alla skannaillaan (Se jossa on @EnableAutoCofiguration) – huomaa tässä sudenkuoppa – jos pistät entityt esim. tuon paketin rinnalle tai yläpuolelle, niitä ei huomioida. Tästä koodista on jätetty paketti pois:

 

@Entity
public class City implements Serializable {

  @Id
  @GeneratedValue
  private Long id;

  @Column(nullable = false)
  private String name;

  @Column(nullable = false)
  private String country;

  protected City() {
    // no-args constructor required by JPA spec
    // this one is protected since it shouldn't be used directly
  }
  public City(String name, String country) {
    this.name = name;
    this.country = country;
  }
  public String getName() {
    return this.name;
  }
  public String getCountry() {
    return this.country;
  }
}

Jep, tätä ei tarvitse rekisteröidä mihinkään. Jos siinä on @MappedSuperclass, @Entity, tai @Embeddable, se huomioidaan automaattisesti. Tätähän pitäisi pystyä vähän manipuloimaan, tallettelemaan kantaan jne. Tehdään rajapinta joka perii CrudRepository kantaluokan:

public interface CityRepository extends CrudRepository<City, Long> {
}

Jep, tosiaan rajapinta. Spring taikoo sen sisään crud metodit automaattisesti. Näin ollen muutetaan meidän Example-luokan REST service esim. tällaiseksi:

@RestController
public class Example {
  @Autowired CityRepository cityRepo;
  @RequestMapping("/")
  String home() {
    City c = new City("Helsinki","Finland");
    cityRepo.save(c);
    return "Hello World2! " + cityRepo.findAll();
  }
}

 

Tuossa kutsutaan cityRepo-luokan (dynaamisesti generoitu rajapinnan pohjalta) save() ja findAll() funktioita jotka ovat automaattisesti olemassa.

No niin, nyt pitäisi vielä luoda tietokanta, datasource, tunnukset, salasanat, sun muut. Paitsi että ei. Yllätyitkö? Spring Boot olettaa että näihin muistinvaraisiin temp kantoihin ei tarvitse luoda datasourceja, tai määritellä sen enempiä tietoja. Riittää että määrittelit riippuvuuden aiemmin. Toisin sanoen voit heti testata sovellusta kunhan olet pudottanut sen sopivaan tomcat serveriin. REST palvelun pitäisi nyt joka kerralla luoda uusi City-olio ja listata se ruudulla.

Aika ketterää. Paljon olisi taas mitä voisi nyt säätää kun alkuun päästiin:

Spring Data antaa mahdollisuuden määritellä lisää kyselyitä suoraan rajapintaan, näin:

public interface CityRepository extends Repository<City, Long> {

    Page<City> findAll(Pageable pageable);

    City findByNameAndCountryAllIgnoringCase(String name, String country);

}

Kun haluat siirtyä testikannasta pysyvämpään testikantaan tai tuotantoon, voit määritellä sen datasource-määritykset application.properties tiedostossa, esim. projektin juuressa:

spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driverClassName=com.mysql.jdbc.Driver

Muista toki myös tuoda se dependencynä pom.xml tiedostossa – jotta saat ajuritkin mukaan.

Kanta luodaan automaattisesti vain H2, HSQL, ja Derby kannalle. Jos haluat sen muillekin, aseta em application.properties tiedostossa ominaisuus päälle:

spring.jpa.hibernate.ddl-auto=create-drop

Muutenkin voit säätää näitä jpa-määrityksiä samaan tapaan:

spring.jpa.database: H2
spring.jpa.show-sql: true

Lisäksi jos pistät luokkapolun juureen tiedoston nimeltä import.sql, sen sisältämät lauseet suoritetaan sovelluksen käynnistyessä – loistava tapa ladata init/testidata sisään. Sen ohella Spring imaisee tiedostot schema.sql, data.sql, ja esim. schema-${platform}.sql ja data-${platform}.sql, ottaen arvonsa spring.datasource.platform muuttujasta.

Spring Boot + Maven = Anna valaistunutta kenkää!

Oh dear, mahtaako tuo sanaleikki aueta kenellekään? Ainakin syytä tarkistaa wikipedia 😉

Kaksi lempivimpaintani, Spring ja Maven, ovat monessa projektissa pohjana. Spring on menossa suuntaan jossa pyritään helpottamaan sen aloitusta ja projekti on nimeltään Spring boot. Pistän tähän taas muistiinpanoja kokeiluistani.

Disclaimer: Vaikka Spring on monen mielestä legacymoskaa ja puhdas Java EE vaihteeksi pop, se tarjoaa edelleenkin muutaman houkuttavan argumentin ketterän projektin kannalta – kuten kyky asentaa softa vaikka tomcat palvelimille (joista löytyy esim. Amazon klusterista helpot valmisasennukset) – kyky ymmärtää ja hallita mekanismeja eri tavalla kuin sovelluspalvelinten mystiset automatiikat toimivat – ja ennen kaikkea mahtava tuki hollywood principlelle ja aop:lle – joka taas tarjoaa hienosti isolaatiota ja sitä myöden yksikkötestattavuutta, sitä myöden nopeita testejä, sitä myöden kehittäjätestejä ja testikattavuutta – sitä myöden speksejä ja toimintavarmuutta – sitä myöden business arvoa. Eli jeah, ei Springin hautajaisia vielä ole pidetty.

Vaihe 1: Tarvitaan kansio projektille, ja pom.xml. Tämmöinen:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
<groupId>com.whatever</groupId>
 <artifactId>myproject</artifactId>
 <version>0.0.1-SNAPSHOT</version>
<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>1.1.0.BUILD-SNAPSHOT</version>
 </parent>
 <!-- (you don't need this if you are using a .RELEASE version) -->
<repositories>
 <repository>
 <id>spring-snapshots</id>
 <url>http://repo.spring.io/snapshot</url>
 <snapshots>
 <enabled>true</enabled>
 </snapshots>
 </repository>
 <repository>
 <id>spring-milestones</id>
 <url>http://repo.spring.io/milestone</url>
 </repository>
 </repositories>
 <pluginRepositories>
 <pluginRepository>
 <id>spring-snapshots</id>
 <url>http://repo.spring.io/snapshot</url>
 </pluginRepository>
 <pluginRepository>
 <id>spring-milestones</id>
 <url>http://repo.spring.io/milestone</url>
 </pluginRepository>
 </pluginRepositories>
</project>

 

Jep, ja vertavuotavalla terällä tässä mennään. Eli bootin epävirallisella snapshot versiolla joka on liikkeessä. Mutta mielummin terällä kuin terän alla.

Päräytetäänpä tässä vaiheessa mvn package. Hmm tottakai esivaatimuksena Java asennettuna (8 tottakai), ja Maven, ja molemmat komentopolussa. Tai voit avata tämän projektin vaikka Netbeans Open toiminnolla, jossa pom tiedostoa onkin mukavampi editoida.

Tämä ei vielä paljoa tee. Lisätään web dependency:

<dependencies>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 </dependencies>

Aja uudelleen mvn package. Nyt paukkuu vähän enemmän latauksia – verkosta ladataan perus-spring-web projektin tarvittavat riippuvuudet. Niitä on paljon (Spring jar helvetti). Ja hups, tässä tuli mukana myös Tomcat.

Päräytetään REST palvelu paikalleen. Naputa tai kopioi tämä maven lähdekoodikansioon (src/main/java – joudut varmaan luomaan tämän kansiorakenteen, koska boot ei sitä automaattisesti tee. Ja Mavenhän vaatii Javan olevan juuri tuon polun alla. Saa tehdä paketin jos erityisesti kutkuttaa.):

import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;

@RestController
@EnableAutoConfiguration
public class Example {

    @RequestMapping("/")
    String home() {
        return "Hello World!";
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Example.class, args);
    }

}

Nyt sitten nopeaan testaukseen. Tarkista ensin ettei ole käynnissä palvelimia portissa 8080 (sammuta Glassfish 😉

Aja seuraava komento:

mvn spring-boot:run

Tämä potkaisee käyntiin tomcatin ja voit kokeilla sitä selaimella osoitteessa http://localhost:8080/

Toimiiko? Mahtavaa, en olisi uskonut. REST servicehän se sieltä kurkkii.

Serverin voi ajaa alas Ctrl+C

Paketointi

Nyt voisi paukauttaa koko roskan-tomcatteineen kaikkineen boot .jar pakettiin. Lisää dependencies-osan alle tällainen plugin:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Ja aja komentoriviltä:

maven package

Tadaa! target-kansioon syntyy uusi .jar joka sisältää tarvittavat riippuvuudet, voit ajaa sen komentoriviltä näin:

java -jar target/myproject-0.0.1-SNAPSHOT.jar

Kun kyllästyt leikkimään, Ctrl+C lopettaa taas.

Spring Boot Java taso on oletuksena 6. Voisi olla kiva käytellä Java 8 uusia piirteitä. Se onnistuu näin pom.xml:ssä:

<properties>
    <java.version>1.8</java.version>
</properties>

Ja jos haluat käyttää myös Tomcat 8 versiota 7:n sijasta:

 <properties>
 <java.version>1.8</java.version>
 <tomcat.version>8.0.3</tomcat.version>
 </properties>

Yksi suositeltava parannus on jakaa initialisointi ja toiminnot eri tiedostoihin, voit tehdä esim. Application-tiedoston sovelluksesi pakettihierarkiaan tähän tapaan:

package com.myorg.myproj;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

}

Ja näin tuo Example-luokka (joka olisi hyvä olla pakettihierarkiassa em. Application luokan alla) näyttäisi tältä:

package com.myorg.myproj.hello;

import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;

@RestController
public class Example {

  @RequestMapping("/")
   String home() {
    return "Hello World2!";
  }

}

Näin on helpompaa lisäillä uusia palveluita.

 

Yhden .jar paketin sovellus on melko kutkuttava, mutta käytännössä on tilanteita joissa olisi myös kiva saada .war ulos, tässä siihen vinkki:

Lisää pom.xml tiedostoon:

<packaging>war</packaging>

Nyt on syytä samantien muuttaa tomcat riippuvuuden scope moodiin ’provided’ – muuten se paketoidaan myös .war paketin sisään toiseen kertaan. Jos sinulla ei ole tätä riippuvuutta vielä, sen voi silti lisätä provided-scopella dependencies-kohtaan:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>

Tässä kohtaa voisit tehdä clean&build ja tiputella tomcat webapps-kansioon, mutta eipä toimi vielä. Alunperäisen ohjeen luokka nimittäin käytti main-metodia initialisointiin, joka sopii kyllä .jar pakettiin hienosti – mutta jota ei enää kukaan kutsu.

Joten lisäämme Servlet-luokan joka perii SpringBootServletInitializer-luokan, ja nykäisee Application-luokan taas käyntiin:

package com.whatever;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;

public class ApplicationInitializer extends SpringBootServletInitializer {

  @Override
  protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
    return application.sources(Application.class);
  }
}

Ja sitten vain mvn clean package, ja target-kansioon putkahtaa geneerinen .war jonka voi asentaa eri palvelimille.

Aika rouheita lisäosia ovat mm. JPA, Spring Data, MongoDB moduulit..

Ja enkunkielinen lähde – kannattaa ehdottomasti lukea esim. pykälä 13.2 koskien pakettien ja luokkien järjestelyn parhaita käytäntöjä.

http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#getting-started-installing-spring-boot

 

 

 

Gitblit ja Glassfish

Minulla on tunnustus. Olen rakastunut palavasti Git versionhallintaan. Kun kerran koukuttuu kykyyn kloonata ja synkata koodivarastoja eri paikkoihin ja paikoista, ja tottuu ajatukseen siitä ettei olekaan välttämättä vain yhtä keskuspalvelinta jonka saatavuudesta kaikki riippuu, ja tottuu helppouteen millä voi tehdä erilaisia kopioita ja kokeiluja ja haaroja…

Niin, ei ole paluuta takaisin. Etsin sopivaa open source tuotetta git repositoryksi, ja vastaan tuli esim. GitLab ja Gitorious. Valitettavasti molemmissa oli haluamalleni alustalle kamalan hankalat ja monimutkaiset ohjeet, ellei sitten satu valmispaketti osumaan juuri täsmälleen paikalleen. Lisäksi esim: GitLab omasi kovasti riippuvuuksia Rubyyn ja senkin osalta oli aika monimutkaista viilata paikalleen – se halusi käyttää omaa versiotaan eikä serverillä jo olevaa.

Joten Java-kaverina nykäisin tietenkin Java-pohjaisen ratkaisun, jossa on enemmän itselle tuttuja osia ja riippuvuuksia. Alustaksi Debian Linux + Glassfish serveri.

GitBlit asennus oli sikäli helppoa että mars osoitteeseen http://gitblit.com/# – lataa .war – pura se haluttuun paikkaan, ja asenna lasikalaan.

asadmin deploy /opt/gitblit-folder

Ja asennettu. Toimii heti samantien heittämällä, mutta toki sitä voi ja kannattaa säätää – esim. muuttaa oletus admin salasana ja käyttöoikeudet. Muutokset tapahtuvat gitblit WEB-INF/data/gitblit.properties tiedoston kautta, ja kun alkushokista on toivuttu, sieltä saa päälle mm. Active Directory/LDAP tuen – testattu ja toimivaksi havaittu. Toisin sanoen saanet repon syömään salasanoja ja tunnuksia ja ryhmiä mistä vain haluamastasi paikasta.

Tämä ei liene isoimpia ja hienoimpia repoja mutta toimi itselleni, ja lisenssinä suuresti rakastettu Apache License 2.0.

Jos AD säädöt kiinnostavat, tässä vähän ideaa niistä (kannattaa myös tosiaan muutella admin salasanaa, ja säätää repositoryt ja tunnusvarastot muualle kuin soveluskansion alle – niin päivitykset ovat helpompia):

realm.authenticationProviders = ldap
realm.ldap.server = ldap://myldap.mydomain.com
realm.ldap.username = mydomain\\myldapreadaccount
realm.ldap.password = myldapreadpassword
realm.ldap.maintainTeams = false
realm.ldap.accountBase = OU=MyOrg,DC=mydomain,DC=com
realm.ldap.accountPattern = (&(objectClass=person)(sAMAccountName=${username}))

Noilla saa perussetit aikaan. Tietenkin tarvitaan tunnus jolla on oikeudet lueksia AD domainia. Koska tähän tiedostoon talletetaan salasana, muista varmistaa ettei siihen ole lukuoikeuksiakaan muilla kuin glassfish tunnuksella. Tässä mallissa ei rajata käyttäjiä sen enempää mitä accountbase rajaa – eli polku mistä tunnukset löytyy, tässä siis com/mydomain/MyOrg. Tässä mallissa tiimejä hallinnoidaan myös Gitblib hallintanäyttöjen kautta. Parannusoptioita on esim. säätää accountPatternia siten että vain tietyt ryhmät hyväksytään, tai kääntää ldap.maintainTeams päälle ja käyttää ldap ryhmiä gitblit tiimeinä, käyttää suojattuja yhteyksiä kommunikointiin, jne.

 

Java 8 + Glassfish 4 asennus Debian Linuxiin – osa 2

Viimeksi asenneltiin Debianiin glassfishiä. Tässä pari edistynyttä niksiä:

32-bit kirjastot 64-bit debianiin

64-bit debian ei välttämättä suostu ajelemaan pkg työkalua jolla voi päivitykset tehdä – se valittaa ia32-libs paketista ja pythonista. Ongelma: kun yrität asentaa paketin, se valittaa riippuvuuksista joita ei olekaan. Ratkaisu tässä:

sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libjpeg62:i386
sudo apt-get install ia32-libs

Nice.

Glassfish asennuksen päivittäminen ajan tasalle.

Nyt voit päivitellä lasikalan uusimpiin kirjastoihin pkg työkalulla esim. näin:

#stop Glassfish
sudo service GlassFish_domain1 stop

# Switch to being user 'glassfish'
sudo su --shell /bin/bash glassfish 

/opt/glassfish4/bin/pkg -R /opt/glassfish4 list
/opt/glassfish4/bin/pkg -R /opt/glassfish4 image-update -v

exit # Revert to being old caffeine-deprived yourself

Muista ajaa kaikki tiedostoja luovat komennot kuten päivitykset ja käynnistykset glassfish-tunnuksella. Jos teet jotain muulla, voit korjata tilanteen päivittelemällä omistajuudet taas glassfish-kansioon ja/tai poistamalla esim. tilapäistiedostot ja cachet. Näin saat omistajuudet kuntoon:

sudo chown glassfish:glassfish -R /opt/glassfish4

 

Oletuksena zip-versio Glassfish ei omaa etähallintaa ja sen admin-salasana on tyhjä, ja master-salasana ’changeit’ – eli ei kovin käyttökelpoinen eikä turvallinen. Korjataanpa nämä.

Glassfish työkalut polkuun

Jatko-operaatioita ajatellen on kätevää lisätä Glassfish bin kansio polkuun, jotta pääset työkaluihin suoraan käsiksi. Tässä vinkit siihen:

#run these as glassfish to generate right ownerships for files
sudo su --shell /bin/bash glassfish

#These will help running the tools:
cd ~
nano .bashrc

#Set up environment for user 'glassfish': edit .bashrc and add this to end:

export PATH="${PATH}":/opt/glassfish4/glassfish/bin

# save file in editor

Testaa voitko ajaa komennon: asadmin. Ellei, kirjaudu ulos ja sisään.

Glassfish master password

Master password on tosiaan oletuksena: changeit – joten muutetaanpa se. Valitettava seuraus on, että muutos päivittää myös avain-ja sertifikaattitiedostoja, joten on taas oltava omistajuuksien kanssa tarkkana. Tässä ohjeistusta:

#if you're glassfish user, exit back to your normal user:
exit

#make sure glassfish is stopped
sudo service GlassFish_domain1 stop

#run these as glassfish to generate right ownerships for files
sudo su --shell /bin/bash glassfish

#Backup default passwords, if you got already master-password file, backup it too:
cd /opt/glassfish4/glassfish

tar cf passwords.orig.tar domains/domain1/config/domain-passwords domains/domain1/config/keystore.jks domains/domain1/config/cacerts.jks

Tässä kohtaa on olemassa varmuuskopio passwords.orig.tar josta löytyy alkuperäiset tiedostot. Nyt muutetaan niitä:

Change master password
asadmin change-master-password --savemasterpassword=true domain1
# Enter the current master password> [By default this is 'changeit']
# Enter the new master password> [e.g. 'Y0uW1llN3v3rGu3ssTh1sL33tP4ssw0rd!']
# Enter the new master password again>
# After entering the new master password twice, there is a pause of several
# seconds. Then you will find new versions of 'master-password'
# 'domain-passwords' 'keystore.jks' and 'cacerts.jks'.

Sitten kokeillaan toimiiko homma – nämä komennot kysyvät salasanaa, anna siihen valitsemasi master password.

# Your chosen master password should unlock both keystores, and reveal
# two certificates in keystore.jks, and many additional ones in cacerts.jks.
keytool -list -v -keystore domains/domain1/config/keystore.jks 
keytool -list -v -keystore domains/domain1/config/cacerts.jks

Ja jos meni jotain pieleen, aina on varmuuskopiot. Toivottavasti ne menivät oikein alunperin, kai tarkistit ne? 😉 Jos kaikki ok, näin voit palauttaa tarvittaessa alkuperäiset tiedostot jos jotain meni pahasti pieleen:

#if you mess up, you can return the backups by going to /opt/glassfish4/glassfish and running: tar -xvf passwords.orig.tar

 Admin-salasana ja etähallinnan aktivointi

Oletuksena tosiaan admin-salasana on tyhjä, ja etähallinta disabloituna. Näin saat korjattua nämä puutteet:

#exit to normal account, sudo start server - it needs to be running
exit
sudo service GlassFish_domain1 start
sudo su --shell /bin/bash glassfish

# enable remote administration for glassfish 

asadmin enable-secure-admin

# ja tässä kohtaa syytä buutata taas palvelin jotta asetukset
# astuvat voimaan

exit
sudo service GlassFish_domain1 restart
sudo su --shell /bin/bash glassfish

Nyt on tilaisuus tallettaa nämä tiedot paikallisesti, siten että linux-tunnuksiltasi ei salasanaa kysellä:

########Store admin password, to enable automatic login to localhost:4848
asadmin login
# Enter admin user name [Enter to accept default]> [Just press Enter]
# Enter admin password> [e.g. whatever you selected earlier]
# Login information relevant to admin user name [admin] for host [localhost] and admin port [4848] stored at [/home/glassfish/.gfclient/pass] successfully.
# Make sure that this file remains protected. Information stored in this file will be used by administration commands to manage associated domain.
# Command login executed successfully.

Eli, tämän tiedoston voit kopioida haluamiesi linux-tunnusten kotihakemiston alle, .gfclient/pass kansioon. Sen oikeudet voit muuttaa niin että vain user omaa oikeudet päästä tähän käsiksi. Muista säätää myös omistajuus oikein, eli omistajaksi ko tunnus.

Voit myös tuikata admin-konsolin vain https-protokollalle:

#Enable https for remote access to admin console
# Requests to http://xxx:4848 are redirected to https://xxx:4848

asadmin set server-config.network-config.protocols.protocol.admin-listener.security-enabled=true

Glassfish käyttämään portteja 80 ja 443 – ilman root oikeuksia

Jos haluat Glassfishin operoivan porteissa 80 ja 443 – mutta et halua ajaa sitä root tunnuksin jotka normaalisti vaaditaan ko porttien käyttöön  – voit tehdä /etc/init.d/GlassFish_domain1 tiedoston loppuun seuraavat reititykset – näin nämä ajetaan root tunnarilla, mutta itse glassfish normaalisti glassfish tunnarilla – ja portit 80 ja 443 ovat käytössä.

iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8080
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 8181

 Sertifikaatit uusiksi

Glassfish oletussertifikaatit ovat tasan samat mitä jokainen käyttää – ei kovin turvallista. Tässä ohjeita miten voit tehdä uudet avaimet omaan käyttöön:

# Ensure that you are running as user 'glassfish'!!
# Ensure GlassFish is stopped

#Inspect keystore.jks
cd /opt/glassfish4/glassfish/domains/domain1/config/
keytool -list -keystore keystore.jks -storepass [whateverismymasterpassword]

#Update keystore.jks

keytool -delete -alias s1as -keystore keystore.jks -storepass [whateverismymasterpassword]

keytool -delete -alias glassfish-instance -keystore keystore.jks -storepass [whateverismymasterpassword]

keytool -genkeypair -alias s1as -dname "CN=myservername,OU=myunit,O=MyOrganization,L=MyTown,C=FI" -keyalg RSA -keysize 2048 -validity 3650 -keystore keystore.jks -keypass [whateverismymasterpassword] -storepass [whateverismymasterpassword]

keytool -genkeypair -alias glassfish-instance -dname "CN=myservername,OU=myunit,O=MyOrganization,L=MyTown,C=FI" -keyalg RSA -keysize 2048 -validity 3650 -keystore keystore.jks -keypass [whateverismymasterpassword] -storepass [whateverismymasterpassword]

#Check keystore.jks

keytool -list -keystore keystore.jks -storepass [whateverismymasterpassword]

#Export certificates from keystore.jks

keytool -exportcert -alias s1as -file s1as.cert -keystore keystore.jks -storepass [whateverismymasterpassword]

keytool -exportcert -alias glassfish-instance -file glassfish-instance.cert -keystore keystore.jks -storepass [whateverismymasterpassword]

#Update cacerts.jks

keytool -delete -alias s1as -keystore cacerts.jks -storepass [whateverismymasterpassword]

keytool -delete -alias glassfish-instance -keystore cacerts.jks -storepass [whateverismymasterpassword]

keytool -importcert -alias s1as -file s1as.cert -keystore cacerts.jks -storepass [whateverismymasterpassword]

#Trust this certificate? [no]: [Enter 'yes']

keytool -importcert -alias glassfish-instance -file glassfish-instance.cert -keystore cacerts.jks -storepass [whateverismymasterpassword]

#Trust this certificate? [no]: [Enter 'yes']

#Check cacerts.jks and tidy up

keytool -list -keystore cacerts.jks -storepass [whateverismymasterpassword]

rm s1as.cert glassfish-instance.cert

exit

Done! Nyt on uudet ja omat avaimet serverillä, ja aiemman vaiheen ansiosta uudella master salasanalla suojattuna. Tässä voi välivaiheena allekirjoituttaa sertifikaattinsa jollain virallisella taholla jos haluaa tuotantokäyttöön sopivan vimpaimen.

Serveriheaderien obfuskointi – äläpä kerro enempää!

Yksi oma lempiaiheeni tietoturvassa on ollut suojata systeemeitä siten että ne eivät kerro tarpeettoman paljon itsestään .- esim. header joka sanoo: X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition 4.0 Java/Oracle Corporation/1.7) – suorastaan kirkuu googlettamaan seuraavaksi haavoittuvuuksia, oletustunnuksia, ja täsmähyökkäysohjelmia. Mitä vähemmän paljastaa tietoa ulospäin sen vaikeampaa on heti tietää mitä hyökkäyksiä soveltaa. Vielä kierompaa on näyttää misinformaatiota – esim. ajaa IIS palvelinta joka väittää olevansa Glassfish tai Glassfishiä joka väittää olevansa Websphere jne.

Tässä komennot joilla voit tukahduttaa näitä headereita. Huom! Ne muuttavat domain1/config/domain.xml tiedostoa, joten syytä taas olla glassfish tunnuksena sisässä (komento whoami kertoo mikä on tunnuksesi). Glassfish serverin tulee olla päällä.

#start glassfish if it's not running
sudo service GlassFish_domain1 start

#Ensure that you are running as user 'glassfish'!!
sudo su --shell /bin/bash glassfish

#Check HTTP headers
# Check HTTP response headers (as shown below) to make sure that there are
# clues as to the server or its version. Look for any mention of
# 'GlassFish', 'X-Powered-By', etc. If necessary:

asadmin create-jvm-options -Dproduct.name=

asadmin set server.network-config.protocols.protocol.http-listener-1.http.xpowered-by=false

asadmin set server.network-config.protocols.protocol.http-listener-2.http.xpowered-by=false

asadmin set server.network-config.protocols.protocol.admin-listener.http.xpowered-by=false

Samalla voit myös tsekata virhesivut, esim. 404 sivua ei löydy kertoo usein paljonkin serveristä. Se on muutenkin hyvä ottaa omaan haltuun. Voit tehdä tämän toki sovelluskohtaisenakin, mutta tässä on serverikohtainen asetus:

asadmin set server.http-service.virtual-server.server.property.send-error_1="code=404 path=/opt/glassfish4/glassfish/globalhtml/404.html reason=Resource_not_found"

Tämä menee domain.xml:ään – jota toki voi käsinkin muokata. Muillekin virheilmoituksille voi tehdä vastaavia. Saatat myös haluta disabloida/poistaa/vaihtaa juuri-kontekstin oletussivun ja sovelluksen.

Lopuksi tietysti buutataan mylly

#Restart to take effect

exit

sudo service GlassFish_domain1 restart

Virtuaalikoneen säädöt ja muisti

Virtuaalikoneen tuunaus on ihan oma taiteenlajinsa – sitä voi harrastaa web-admin konsolista, tai domain.xml tiedostoa editoimalla, mutta onnistuu se komentoriviltäkin:

#List current JVM options

asadmin list-jvm-options

# Now update some important JVM settings. 
# Of course use memory as you need to, so if you like 64GB max heap, go for it!

asadmin delete-jvm-options -Xmx512m
asadmin create-jvm-options -Xmx2048m
asadmin create-jvm-options -Xms1024m

# and run with -client or -server optimizations
# -client boots up faster, server optimizes tighter on longer run
# -client is now the default, so if you like -server:

asadmin delete-jvm-options -client
asadmin create-jvm-options -server

Siinä kaikki tällä erää. Moderni sovelluspalvelin on sillä tavalla kiehtova paketti että se sisältää hurjasti piirteitä, palveluita, ja yksityiskohtia. Monet näistä periaatteista pätevät hyvinkin muihinkin servereihin, kuten Tomcat ja JBOSS, ja miksei myös WebSphere/Weblogic. Toivottavasti näistä on apua.

 

Java 8 + Glassfish 4 asennus Debian Linuxiin – osa 1

Ajattelin kirjailla kokemuksia Glassfish Linux asennuksesta, 64-bittiseen Debianiin. Osaan 1 laitan perusohjeistusta, osaan 2 tulee sudenkuoppia ja yksityiskohtia.

Olen kirjoitellut jo aiemmin tästä Raspberry Pi vinkkelistä, mutta tässä vähän lisää yksityiskohtia. Tarinaa olen haalinut hyvistä muista blogeista, ja pistän linkkiä loppuun, mutta ainakin paras niistä on häviämässä netistä joten pistän tämän itsellekin talteen.

Testailin juuri asennuksia AWS pilvipalvelun 64-bittiseen Ubuntuun, ja näyttäisi pelittävän ihan heittämällä myös sinne.

Alkuun tarvitaan Java, JDK 7 tai 8, itsellä tietenkin 8. Tästä taisin juuri kirjailla, miten Java on lisenssiteknisistä syistä vähän jännä asennella. Mutta tässä Oracle JDK 8 litaniat:

#make sure you're up-to-date
sudo apt-get update
sudo apt-get upgrade

#install oracle jdk 8
sudo echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | sudo tee /etc/apt/sources.list.d/webupd8team-java.list
sudo echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | sudo tee -a /etc/apt/sources.list.d/webupd8team-java.list
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys EEA14886
sudo apt-get update
sudo apt-get install oracle-java8-installer
java -version

Löytyykö sieltä Java? Hienoa!

Glassfish oletusasennus käyttelee root tunnaria joka on vähän hyi hyi. Tämän vuoksi meidän kannattaa tehdä prosessille uusi käyttäjätunnus, glassfish. Jos lähdet tälle polulle, muista että muokatessa glassfish kansion tiedostoja tai ajaessa prosessia, se tulisi aina tehdä glassfish tunnarien alla. Muuten voi syntyä tiedostoja joihin glassfish-serveriprosessi ei omaa pääsyä, esim. temp, cache, jne. Sen voi onneksi aina korjata chownaamalla koko glassfish hierarkian.

#Add a new user called 'glassfish' with group 'glassfish'
sudo adduser --system --group --home /home/glassfish [--shell /bin/bash] glassfish
#Enable an existing user to be a GlassFish administrator. Repeat for additional users.
sudo usermod -a -G glassfish existingUserAccountName
# can check your groups with:
groups #list should include 'glassfish' - remember to relogin here!

Sitten tuikataan Glassfish 4 zip release paikalleen:

#get glassfish 4 release
cd ~
wget http://download.java.net/glassfish/4.0/release/glassfish-4.0.zip

sudo apt-get install unzip
sudo unzip -d /opt/ glassfish-4.0.zip 

#glassfish is owner of this folder
sudo chown -R glassfish:glassfish /opt/glassfish4
#glassfish can freely access bin folders and autodeploy
sudo chmod -R ug+rw /opt/glassfish4
#glassfish can execute binaries
sudo chmod -R ug+rwx /opt/glassfish4/bin
sudo chmod -R ug+rwx /opt/glassfish4/glassfish/bin
#others are not allowed to access glassfish
sudo chmod -R o-rwx /opt/glassfish4/

Seuraavaksi fiksataan proaktiivisesti yksi asia: Java seiskan viimeisimmissä päivityksissä – ja myös Java 8:ssa – on tiukennettu tietoturva-asetuksia. Tästä johtuen esim. Derby tietokantaa ei saa käyntiin ellei sitä salli policyissä. Tässä on karu tapa lisätä kaikelle ajettavalle Java-koodille oikeus käyttää Derby-porttia. Tätä voi tiukentaa halutessaan esim. kansiokohtaiseksi:

#edit $JAVA_HOME/jre/lib/security/java.policy, insert following between existing grant blocks:
grant {
 // allows Derby to claim port 1527
 permission java.net.SocketPermission "localhost:1527", "listen,resolve";
};

Seuraavaksi päräytetään vaihtaen glassfish tunnarille – jotta luotavat tiedostot menevät oikeille omistussuhteille ja oikeuksille. Käynnistetään derby ja glassfish jotta voidaan todeta toimiiko kaikki:

#now, run glassfish server & database
sudo su --shell /bin/bash glassfish
whoami # should say 'glassfish'
/opt/glassfish4/bin/asadmin start-database
/opt/glassfish4/bin/asadmin start-domain domain1

Kaikki hyvin? Voit testata varmuudeksi löytääkö selain portit:

http://localhost:8080

https://localhost:8181

http://localhost:4848

Jos näyttää hyvältä, on aika sammutella Glassfish, ja poistua glassfish tunnuksesta takaisin omaan:

/opt/glassfish4/bin/asadmin stop-domain domain1
/opt/glassfish4/bin/asadmin stop-database
exit

Jos kaikki löytyy, voit seuraavaksi asentaa tämän palveluna. Aloitetaan Glassfishin omalla työkalulla:

sudo /opt/glassfish4/bin/asadmin create-service

Valitettavasti käynnistysscripti jonka tämä luo on täyttä moskaa. Jos käynnistäisit nyt koneen, se ei toimisi. Siinä on monia virheitä:
– init.d scriptit ajetaan ennenkuin esim. profile tiedostot ja niiden pathit on käsitelty. Näin mitään työkaluja ei ole patheissa vaan pathit pitää exportoida erikseen, tai viitata koko absoluuttisilla poluilla
– scriptistä puuttuu service header tiedostot jotka estävät sen toiminnan
– oletusscripti ajelee serveriä roottina, joten jos sinne pujahtaa pätkä ilkeämmän puoleista koodia jonkun haavoittuvuuden vuoksi…. WORLD DOMINATION!!!
– derby kanta olisi myös hyvä käynnistellä ja sammutella
– olisi kiva saada status tietoja

Joten, korvataan scripti parannetulla versiolla. Avaa editori:

sudo nano /etc/init.d/GlassFish_domain1

Jyrää vanha moska tällä, säädä tarpeen mukaan vastaamaan omia polkujasi tai tarpeitasi:

#! /bin/sh
### BEGIN INIT INFO
# Provides: glassfish
# Required-Start: $remote_fs $network $syslog
# Required-Stop: $remote_fs $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Starts GlassFish
# Description: Starts GlassFish application server
### END INIT INFO
# We need this, since NetworkServerControl uses $JAVA_HOME to find java
export JAVA_HOME="/usr/lib/jvm/java-8-oracle"
GLASSFISH=/opt/glassfish4
DERBY_BIN=/opt/glassfish4/javadb/bin
case "$1" in
start)
 echo "Starting GlassFish from $GLASSFISH"
 sudo -u glassfish -E "$GLASSFISH/bin/asadmin" start-database
 sudo -u glassfish -E "$GLASSFISH/bin/asadmin" start-domain domain1
 ;;
stop)
 echo "Stopping GlassFish from $GLASSFISH"
 sudo -u glassfish -E "$GLASSFISH/bin/asadmin" stop-domain domain1
 sudo -u glassfish -E "$GLASSFISH/bin/asadmin" stop-database
 ;;
restart)
 $0 stop
 $0 start
 ;;
status)
 echo "# GlassFish at $GLASSFISH:"
 sudo -u glassfish -E "$GLASSFISH/bin/asadmin" list-domains | grep -v Command
 sudo -u glassfish -E "$GLASSFISH/bin/asadmin" list-domains | grep -q "domain1 running"
 if [ $? -eq 0 ]; then
 sudo -u glassfish -E "$GLASSFISH/bin/asadmin" uptime | grep -v Command
 echo "\n# Deployed applications:"
 sudo -u glassfish -E "$GLASSFISH/bin/asadmin" list-applications --long=true --resources | grep -v Command echo "\n# JDBC resources:"
 sudo -u glassfish -E "$GLASSFISH/bin/asadmin" list-jdbc-resources | grep "jdbc/"
 fi
 echo "\n# Derby:"
 sudo -u glassfish -E "$DERBY_BIN/NetworkServerControl" ping | sed "s/^.* : //"
 ;;
*)
 echo "Usage: $0 {start|stop|restart|status}"
 exit 1
 ;;
esac
exit 0

Nättiä kuin kesäheinän teko. Tässä voi olla vielä parannettavaa mutta itselle ainakin varsin tyydyttävä toiminta. Pistä kommenttia jos tulee parannuksia itsellesi mieleen!

Aja seuraavaksi palvelun testi:

#test that it works:
/etc/init.d/GlassFish_domain1 start
/etc/init.d/GlassFish_domain1 status
/etc/init.d/GlassFish_domain1 stop

Jup, status voi heittää vähän autentikointivirheitä koska kannassa ei ole glassfish käyttäjää. Mutta palvelun pitäisi käynnistyä ja sammua tätä kautta. Seuraavaksi uudelleenasennetaan tämä palveluna, ja testataan että se toimii edelleen:

#cool, now let's update the autorun part with the script:
sudo update-rc.d GlassFish_domain1 defaults

#test that it works as a service
sudo service GlassFish_domain1 start

#if it seems to work, test that it REALLY works: 
sudo reboot

#and verify by connecting to http://thisservername:8080 and http://thisservername:4848

Nice? Yeah, hyvin pelittää. Ensi kerralla voitaisiin näperrellä servosta turvallisempi ja tehokkaampi.

Linuxin kanssa on kiva pitkästä aikaa puuhailla. Niissä servereissä on erona winkku servoihin että kun johonkin ei koske pitkään aikaan ja sen melkein unohtaa, se toimii yhtä hienosti kuin asennuspäivänä 😉

Linkkivinkki mitä itse eniten käytin: http://www.physics.usyd.edu.au/~rennie/glassfish.html

Oracle Java 8 Debianiin

Jep jep, lisenssiyhteensopivuusongelmista johtuen Oraclen virallinen Java 8 on hieman mutkatonta hankalampaa saada Debianiin. Samoin Java 7. Vaihtoehto on toki asentaa OpenJDK. Mutta sattuneesta syystä törmäsin artikkeliin jossa neuvotaan miten myös Oracle Java 8 on asennettavissa – sekä JRE että JDK.

Jippo on tässä:

su -
echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | tee /etc/apt/sources.list.d/webupd8team-java.list
echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | tee -a /etc/apt/sources.list.d/webupd8team-java.list
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys EEA14886
apt-get update
apt-get install oracle-java8-installer
exit

Jep, ei sen kummempaa.Tietysti voit myös pistää sudo komennon joka komentosarjan alkuun.

Jippo on siinä että repositorystä ei löydy varsinaisesti Java 8 versiota – vaan vain installer. Kun sen asentaa ja ajaa, tulee lisenssiehtojen hyväksyntädialogi. Ja sitten tippuu varsinainen Java.

Ja tietysti tarkistetaan mikä Java on kyseessä komennolla:

java -version

Tähän asennukseen sisältyy tietysti myös automaattiset päivitykset.

Vinkin lähde:

http://www.webupd8.org/2014/03/how-to-install-oracle-java-8-in-debian.html